Merge "Do not hide media metadata when bypassing LS" into qt-r1-dev
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
index 83c034b..21c6bd2 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -68,9 +68,10 @@
};
bool VendorIsQOrLater() {
- // STOPSHIP(b/119390857): Check api version once Q sdk version is finalized
- std::string version = android::base::GetProperty("ro.vndk.version", "Q");
- return version == "Q" || version == "q";
+ constexpr int kQSdkVersion = 29;
+ int version = std::atoi(android::base::GetProperty("ro.vndk.version", "29").data());
+ // If the string cannot be parsed, it is a development sdk codename.
+ return version >= kQSdkVersion || version == 0;
}
Result<std::unique_ptr<std::vector<std::string>>> FindApkFiles(const std::vector<std::string>& dirs,
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index d59d0e2..6bedfcd 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -263,6 +263,7 @@
"tests/e2e/Anomaly_duration_sum_e2e_test.cpp",
"tests/e2e/ConfigTtl_e2e_test.cpp",
"tests/e2e/PartialBucket_e2e_test.cpp",
+ "tests/e2e/DurationMetric_e2e_test.cpp",
"tests/shell/ShellSubscriber_test.cpp",
],
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 92aa425..ce75f78 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -273,6 +273,13 @@
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
+
+ FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
+ FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 615c7f2..96fbf7f 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -220,7 +220,7 @@
trueConditionDimensions.end()) {
for (auto& condIt : whatIt.second) {
condIt.second->onConditionChanged(
- currentUnSlicedPartCondition, eventTime);
+ currentUnSlicedPartCondition, eventTime);
}
}
}
@@ -314,7 +314,7 @@
auto condIt = whatIt.second.find(trueDim);
if (condIt != whatIt.second.end()) {
condIt->second->onConditionChanged(
- currentUnSlicedPartCondition, eventTime);
+ currentUnSlicedPartCondition, eventTime);
} else {
if (mMetric2ConditionLinks.size() == 0 ||
trueDim.contains(linkedConditionDimensionKey)) {
@@ -338,32 +338,25 @@
}
}
-void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
- const int64_t eventTime) {
- VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
- flushIfNeededLocked(eventTime);
-
- if (!mConditionSliced) {
- return;
- }
-
+void DurationMetricProducer::onSlicedConditionMayChangeInternalLocked(bool overallCondition,
+ const int64_t eventTimeNs) {
bool changeDimTrackable = mWizard->IsChangedDimensionTrackable(mConditionTrackerIndex);
if (changeDimTrackable && mHasLinksToAllConditionDimensionsInTracker &&
mDimensionsInCondition.empty()) {
- onSlicedConditionMayChangeLocked_opt1(overallCondition, eventTime);
+ onSlicedConditionMayChangeLocked_opt1(overallCondition, eventTimeNs);
return;
}
if (changeDimTrackable && mSameConditionDimensionsInTracker &&
mMetric2ConditionLinks.size() <= 1) {
- onSlicedConditionMayChangeLocked_opt2(overallCondition, eventTime);
+ onSlicedConditionMayChangeLocked_opt2(overallCondition, eventTimeNs);
return;
}
// Now for each of the on-going event, check if the condition has changed for them.
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
for (auto& pair : whatIt.second) {
- pair.second->onSlicedConditionMayChange(overallCondition, eventTime);
+ pair.second->onSlicedConditionMayChange(overallCondition, eventTimeNs);
}
}
@@ -389,10 +382,10 @@
continue;
}
unique_ptr<DurationTracker> newTracker =
- whatIt.second.begin()->second->clone(eventTime);
+ whatIt.second.begin()->second->clone(eventTimeNs);
if (newTracker != nullptr) {
newTracker->setEventKey(MetricDimensionKey(newEventKey));
- newTracker->onSlicedConditionMayChange(overallCondition, eventTime);
+ newTracker->onSlicedConditionMayChange(overallCondition, eventTimeNs);
whatIt.second[conditionDimension] = std::move(newTracker);
}
}
@@ -418,10 +411,10 @@
if (hitGuardRailLocked(newEventKey)) {
continue;
}
- auto newTracker = whatIt.second.begin()->second->clone(eventTime);
+ auto newTracker = whatIt.second.begin()->second->clone(eventTimeNs);
if (newTracker != nullptr) {
newTracker->setEventKey(newEventKey);
- newTracker->onSlicedConditionMayChange(overallCondition, eventTime);
+ newTracker->onSlicedConditionMayChange(overallCondition, eventTimeNs);
whatIt.second[conditionDimension] = std::move(newTracker);
}
}
@@ -430,10 +423,61 @@
}
}
+void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
+ const int64_t eventTime) {
+ VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
+
+ if (!mIsActive) {
+ return;
+ }
+
+ flushIfNeededLocked(eventTime);
+
+ if (!mConditionSliced) {
+ return;
+ }
+
+ onSlicedConditionMayChangeInternalLocked(overallCondition, eventTime);
+}
+
+void DurationMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) {
+ MetricProducer::onActiveStateChangedLocked(eventTimeNs);
+
+ if (!mConditionSliced) {
+ if (ConditionState::kTrue != mCondition) {
+ return;
+ }
+
+ if (mIsActive) {
+ flushIfNeededLocked(eventTimeNs);
+ }
+
+ for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
+ for (auto& pair : whatIt.second) {
+ pair.second->onConditionChanged(mIsActive, eventTimeNs);
+ }
+ }
+ } else if (mIsActive) {
+ flushIfNeededLocked(eventTimeNs);
+ onSlicedConditionMayChangeInternalLocked(mIsActive, eventTimeNs);
+ } else { // mConditionSliced == true && !mIsActive
+ for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
+ for (auto& pair : whatIt.second) {
+ pair.second->onConditionChanged(mIsActive, eventTimeNs);
+ }
+ }
+ }
+}
+
void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
const int64_t eventTime) {
VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
+
+ if (!mIsActive) {
+ return;
+ }
+
flushIfNeededLocked(eventTime);
for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
for (auto& pair : whatIt.second) {
@@ -696,7 +740,9 @@
return;
}
- flushIfNeededLocked(event.GetElapsedTimestampNs());
+ if (mIsActive) {
+ flushIfNeededLocked(event.GetElapsedTimestampNs());
+ }
// Handles Stopall events.
if (matcherIndex == mStopAllIndex) {
@@ -767,6 +813,8 @@
}
}
+ condition = condition && mIsActive;
+
if (dimensionKeysInCondition.empty()) {
handleStartEvent(MetricDimensionKey(dimensionInWhat, DEFAULT_DIMENSION_KEY),
conditionKey, condition, event);
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index f711df2..56c9fd6 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -73,9 +73,15 @@
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
+ // Internal interface to handle active state change.
+ void onActiveStateChangedLocked(const int64_t& eventTimeNs) override;
+
// Internal interface to handle sliced condition change.
void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
+ void onSlicedConditionMayChangeInternalLocked(bool overallCondition,
+ const int64_t eventTimeNs);
+
void onSlicedConditionMayChangeLocked_opt1(bool overallCondition, const int64_t eventTime);
void onSlicedConditionMayChangeLocked_opt2(bool overallCondition, const int64_t eventTime);
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 41000da..7a87f03 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -364,11 +364,27 @@
}
}
+void GaugeMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) {
+ MetricProducer::onActiveStateChangedLocked(eventTimeNs);
+ if (ConditionState::kTrue != mCondition || !mIsPulled) {
+ return;
+ }
+ if (mTriggerAtomId == -1 || (mIsActive && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE)) {
+ pullAndMatchEventsLocked(eventTimeNs);
+ }
+
+}
+
void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet,
const int64_t eventTimeNs) {
VLOG("GaugeMetric %lld onConditionChanged", (long long)mMetricId);
- flushIfNeededLocked(eventTimeNs);
+
mCondition = conditionMet ? ConditionState::kTrue : ConditionState::kFalse;
+ if (!mIsActive) {
+ return;
+ }
+
+ flushIfNeededLocked(eventTimeNs);
if (mIsPulled && mTriggerAtomId == -1) {
pullAndMatchEventsLocked(eventTimeNs);
} // else: Push mode. No need to proactively pull the gauge data.
@@ -378,10 +394,14 @@
const int64_t eventTimeNs) {
VLOG("GaugeMetric %lld onSlicedConditionMayChange overall condition %d", (long long)mMetricId,
overallCondition);
+ mCondition = overallCondition ? ConditionState::kTrue : ConditionState::kFalse;
+ if (!mIsActive) {
+ return;
+ }
+
flushIfNeededLocked(eventTimeNs);
// If the condition is sliced, mCondition is true if any of the dimensions is true. And we will
// pull for every dimension.
- mCondition = overallCondition ? ConditionState::kTrue : ConditionState::kFalse;
if (mIsPulled && mTriggerAtomId == -1) {
pullAndMatchEventsLocked(eventTimeNs);
} // else: Push mode. No need to proactively pull the gauge data.
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 1b43d43..a612adf 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -106,6 +106,9 @@
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
+ // Internal interface to handle active state change.
+ void onActiveStateChangedLocked(const int64_t& eventTimeNs) override;
+
// Internal interface to handle sliced condition change.
void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index d913427..8cbc9c4 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -107,7 +107,7 @@
}
mIsActive = evaluateActiveStateLocked(elapsedTimestampNs);
if (!mIsActive) {
- flushLocked(elapsedTimestampNs);
+ onActiveStateChangedLocked(elapsedTimestampNs);
}
}
@@ -143,7 +143,11 @@
}
activation->start_ns = elapsedTimestampNs;
activation->state = ActivationState::kActive;
+ bool oldActiveState = mIsActive;
mIsActive = true;
+ if (!oldActiveState) { // Metric went from not active to active.
+ onActiveStateChangedLocked(elapsedTimestampNs);
+ }
}
void MetricProducer::cancelEventActivationLocked(int deactivationTrackerIndex) {
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 3ddbef4..aa75761 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -132,23 +132,17 @@
// Consume the parsed stats log entry that already matched the "what" of the metric.
void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
std::lock_guard<std::mutex> lock(mMutex);
- if (mIsActive) {
- onMatchedLogEventLocked(matcherIndex, event);
- }
+ onMatchedLogEventLocked(matcherIndex, event);
}
void onConditionChanged(const bool condition, const int64_t eventTime) {
std::lock_guard<std::mutex> lock(mMutex);
- if (mIsActive) {
- onConditionChangedLocked(condition, eventTime);
- }
+ onConditionChangedLocked(condition, eventTime);
}
void onSlicedConditionMayChange(bool overallCondition, const int64_t eventTime) {
std::lock_guard<std::mutex> lock(mMutex);
- if (mIsActive) {
- onSlicedConditionMayChangeLocked(overallCondition, eventTime);
- }
+ onSlicedConditionMayChangeLocked(overallCondition, eventTime);
}
bool isConditionSliced() const {
@@ -304,12 +298,18 @@
* bucket's end timestamp, than we flush up to the end of the latest full bucket; otherwise,
* we assume that we want to flush a partial bucket. The bucket start timestamp and bucket
* number are not changed by this function. This method should only be called by
- * flushIfNeededLocked or the app upgrade handler; the caller MUST update the bucket timestamp
- * and bucket number as needed.
+ * flushIfNeededLocked or flushLocked or the app upgrade handler; the caller MUST update the
+ * bucket timestamp and bucket number as needed.
*/
virtual void flushCurrentBucketLocked(const int64_t& eventTimeNs,
const int64_t& nextBucketStartTimeNs) {};
+ virtual void onActiveStateChangedLocked(const int64_t& eventTimeNs) {
+ if (!mIsActive) {
+ flushLocked(eventTimeNs);
+ }
+ }
+
// Convenience to compute the current bucket's end time, which is always aligned with the
// start time of the metric.
int64_t getCurrentBucketEndTimeNs() const {
@@ -412,6 +412,13 @@
bool mIsActive;
+ FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
+ FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
+
FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations);
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index da3be06..6fc2c13 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -291,6 +291,13 @@
FRIEND_TEST(StatsLogProcessorTest,
TestActivationOnBootMultipleActivationsDifferentActivationTypes);
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
+
+ FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
+ FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
+ FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 17f2994..01362b6 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -109,7 +109,7 @@
mMaxPullDelayNs(metric.max_pull_delay_sec() > 0 ? metric.max_pull_delay_sec() * NS_PER_SEC
: StatsdStats::kPullMaxDelayNs),
mSplitBucketForAppUpgrade(metric.split_bucket_for_app_upgrade()),
- mConditionTimer(mCondition == ConditionState::kTrue, timeBaseNs) {
+ mConditionTimer(mIsActive && mCondition == ConditionState::kTrue, timeBaseNs) {
int64_t bucketSizeMills = 0;
if (metric.has_bucket()) {
bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
@@ -364,58 +364,98 @@
mHasGlobalBase = false;
}
-void ValueMetricProducer::onConditionChangedLocked(const bool condition,
- const int64_t eventTimeNs) {
+// Handle active state change. Active state change is treated like a condition change:
+// - drop bucket if active state change event arrives too late
+// - if condition is true, pull data on active state changes
+// - ConditionTimer tracks changes based on AND of condition and active state.
+void ValueMetricProducer::onActiveStateChangedLocked(const int64_t& eventTimeNs) {
bool isEventTooLate = eventTimeNs < mCurrentBucketStartTimeNs;
- if (!isEventTooLate) {
- if (mCondition == ConditionState::kUnknown) {
- // If the condition was unknown, we mark the bucket as invalid since the bucket will
- // contain partial data. For instance, the condition change might happen close to the
- // end of the bucket and we might miss lots of data.
- //
- // We still want to pull to set the base.
- invalidateCurrentBucket();
- }
-
- // Pull on condition changes.
- ConditionState newCondition = condition ? ConditionState::kTrue : ConditionState::kFalse;
- bool conditionChanged =
- (mCondition == ConditionState::kTrue && newCondition == ConditionState::kFalse)
- || (mCondition == ConditionState::kFalse && newCondition == ConditionState::kTrue);
- // We do not need to pull when we go from unknown to false.
- //
- // We also pull if the condition was already true in order to be able to flush the bucket at
- // the end if needed.
- //
- // onConditionChangedLocked might happen on bucket boundaries if this is called before
- // #onDataPulled.
- if (mIsPulled && (conditionChanged || condition)) {
- pullAndMatchEventsLocked(eventTimeNs, newCondition);
- }
-
- // When condition change from true to false, clear diff base but don't
- // reset other counters as we may accumulate more value in the bucket.
- if (mUseDiff && mCondition == ConditionState::kTrue
- && newCondition == ConditionState::kFalse) {
- resetBase();
- }
- mCondition = newCondition;
- } else {
- VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
- (long long)mCurrentBucketStartTimeNs);
- StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId);
+ if (ConditionState::kTrue == mCondition && isEventTooLate) {
+ // Drop bucket because event arrived too late, ie. we are missing data for this bucket.
invalidateCurrentBucket();
- // Something weird happened. If we received another event in the future, the condition might
- // be wrong.
- mCondition = initialCondition(mConditionTrackerIndex);
}
- // This part should alway be called.
+ // Call parent method once we've verified the validity of current bucket.
+ MetricProducer::onActiveStateChangedLocked(eventTimeNs);
+
+ if (ConditionState::kTrue != mCondition) {
+ return;
+ }
+
+ // Pull on active state changes.
+ if (!isEventTooLate) {
+ if (mIsPulled) {
+ pullAndMatchEventsLocked(eventTimeNs, mCondition);
+ }
+ // When active state changes from true to false, clear diff base but don't
+ // reset other counters as we may accumulate more value in the bucket.
+ if (mUseDiff && !mIsActive) {
+ resetBase();
+ }
+ }
+
flushIfNeededLocked(eventTimeNs);
- mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
+
+ // Let condition timer know of new active state.
+ mConditionTimer.onConditionChanged(mIsActive, eventTimeNs);
}
-void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs, ConditionState condition) {
+void ValueMetricProducer::onConditionChangedLocked(const bool condition,
+ const int64_t eventTimeNs) {
+ ConditionState newCondition = condition ? ConditionState::kTrue : ConditionState::kFalse;
+ bool isEventTooLate = eventTimeNs < mCurrentBucketStartTimeNs;
+
+ if (mIsActive) {
+ if (isEventTooLate) {
+ VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
+ (long long)mCurrentBucketStartTimeNs);
+ StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId);
+ invalidateCurrentBucket();
+ } else {
+ if (mCondition == ConditionState::kUnknown) {
+ // If the condition was unknown, we mark the bucket as invalid since the bucket will
+ // contain partial data. For instance, the condition change might happen close to
+ // the end of the bucket and we might miss lots of data.
+ //
+ // We still want to pull to set the base.
+ invalidateCurrentBucket();
+ }
+
+ // Pull on condition changes.
+ bool conditionChanged =
+ (mCondition == ConditionState::kTrue && newCondition == ConditionState::kFalse)
+ || (mCondition == ConditionState::kFalse &&
+ newCondition == ConditionState::kTrue);
+ // We do not need to pull when we go from unknown to false.
+ //
+ // We also pull if the condition was already true in order to be able to flush the
+ // bucket at the end if needed.
+ //
+ // onConditionChangedLocked might happen on bucket boundaries if this is called before
+ // #onDataPulled.
+ if (mIsPulled && (conditionChanged || condition)) {
+ pullAndMatchEventsLocked(eventTimeNs, newCondition);
+ }
+
+ // When condition change from true to false, clear diff base but don't
+ // reset other counters as we may accumulate more value in the bucket.
+ if (mUseDiff && mCondition == ConditionState::kTrue
+ && newCondition == ConditionState::kFalse) {
+ resetBase();
+ }
+ }
+ }
+
+ mCondition = isEventTooLate ? initialCondition(mConditionTrackerIndex) : newCondition;
+
+ if (mIsActive) {
+ flushIfNeededLocked(eventTimeNs);
+ mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
+ }
+}
+
+void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs,
+ ConditionState condition) {
vector<std::shared_ptr<LogEvent>> allData;
if (!mPullerManager->Pull(mPullTagId, &allData)) {
ALOGE("Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index de01e72..739f6ef 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -93,6 +93,9 @@
android::util::ProtoOutputStream* protoOutput) override;
void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
+ // Internal interface to handle active state change.
+ void onActiveStateChangedLocked(const int64_t& eventTimeNs) override;
+
// Internal interface to handle condition change.
void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 0a9161d..9b48a02 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -442,7 +442,7 @@
if (erase_data) {
remove(fullPathName.c_str());
- } else if (output.mIsHistory && !isAdb) {
+ } else if (!output.mIsHistory && !isAdb) {
// This means a real data owner has called to get this data. But the config says it
// wants to keep a local history. So now this file must be renamed as a history file.
// So that next time, when owner calls getData() again, this data won't be uploaded
diff --git a/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
new file mode 100644
index 0000000..5da0fca
--- /dev/null
+++ b/cmds/statsd/tests/e2e/DurationMetric_e2e_test.cpp
@@ -0,0 +1,717 @@
+// Copyright (C) 2019 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 <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+TEST(DurationMetricE2eTest, TestOneBucket) {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+ auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = screenOnMatcher;
+ *config.add_atom_matcher() = screenOffMatcher;
+
+ auto durationPredicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = durationPredicate;
+
+ int64_t metricId = 123456;
+ auto durationMetric = config.add_duration_metric();
+ durationMetric->set_id(metricId);
+ durationMetric->set_what(durationPredicate.id());
+ durationMetric->set_bucket(FIVE_MINUTES);
+ durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
+
+
+ const int64_t baseTimeNs = 0; // 0:00
+ const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01
+ const int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL;
+
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
+
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_TRUE(metricProducer->mIsActive);
+
+ std::unique_ptr<LogEvent> event;
+
+ // Screen is off at start of bucket.
+ event = CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, configAddedTimeNs); // 0:01
+ processor->OnLogEvent(event.get());
+
+ // Turn screen on.
+ const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs);
+ processor->OnLogEvent(event.get());
+
+ // Turn off screen 30 seconds after turning on.
+ const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, durationEndNs);
+ processor->OnLogEvent(event.get());
+
+ event = CreateScreenBrightnessChangedEvent(64, durationEndNs + 1 * NS_PER_SEC); // 0:42
+ processor->OnLogEvent(event.get());
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true,
+ ADB_DUMP, FAST, &buffer); // 5:01
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStartEndTimestamp(&reports);
+ EXPECT_EQ(1, reports.reports_size());
+ EXPECT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
+
+ const StatsLogReport::DurationMetricDataWrapper& durationMetrics =
+ reports.reports(0).metrics(0).duration_metrics();
+ EXPECT_EQ(1, durationMetrics.data_size());
+
+ auto data = durationMetrics.data(0);
+ EXPECT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(durationEndNs - durationStartNs, data.bucket_info(0).duration_nanos());
+ EXPECT_EQ(configAddedTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+}
+
+TEST(DurationMetricE2eTest, TestTwoBuckets) {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+ auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = screenOnMatcher;
+ *config.add_atom_matcher() = screenOffMatcher;
+
+ auto durationPredicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = durationPredicate;
+
+ int64_t metricId = 123456;
+ auto durationMetric = config.add_duration_metric();
+ durationMetric->set_id(metricId);
+ durationMetric->set_what(durationPredicate.id());
+ durationMetric->set_bucket(FIVE_MINUTES);
+ durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
+
+
+ const int64_t baseTimeNs = 0; // 0:00
+ const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01
+ const int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL;
+
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
+
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_TRUE(metricProducer->mIsActive);
+
+ std::unique_ptr<LogEvent> event;
+
+ // Screen is off at start of bucket.
+ event = CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, configAddedTimeNs); // 0:01
+ processor->OnLogEvent(event.get());
+
+ // Turn screen on.
+ const int64_t durationStartNs = configAddedTimeNs + 10 * NS_PER_SEC; // 0:11
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs);
+ processor->OnLogEvent(event.get());
+
+ // Turn off screen 30 seconds after turning on.
+ const int64_t durationEndNs = durationStartNs + 30 * NS_PER_SEC; // 0:41
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, durationEndNs);
+ processor->OnLogEvent(event.get());
+
+ event = CreateScreenBrightnessChangedEvent(64, durationEndNs + 1 * NS_PER_SEC); // 0:42
+ processor->OnLogEvent(event.get());
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor->onDumpReport(cfgKey, configAddedTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC, false, true,
+ ADB_DUMP, FAST, &buffer); // 10:01
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStartEndTimestamp(&reports);
+ EXPECT_EQ(1, reports.reports_size());
+ EXPECT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
+
+ const StatsLogReport::DurationMetricDataWrapper& durationMetrics =
+ reports.reports(0).metrics(0).duration_metrics();
+ EXPECT_EQ(1, durationMetrics.data_size());
+
+ auto data = durationMetrics.data(0);
+ EXPECT_EQ(1, data.bucket_info_size());
+
+ auto bucketInfo = data.bucket_info(0);
+ EXPECT_EQ(0, bucketInfo.bucket_num());
+ EXPECT_EQ(durationEndNs - durationStartNs, bucketInfo.duration_nanos());
+ EXPECT_EQ(configAddedTimeNs, bucketInfo.start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
+}
+
+TEST(DurationMetricE2eTest, TestWithActivation) {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+ auto screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
+ auto crashMatcher = CreateProcessCrashAtomMatcher();
+ *config.add_atom_matcher() = screenOnMatcher;
+ *config.add_atom_matcher() = screenOffMatcher;
+ *config.add_atom_matcher() = crashMatcher;
+
+ auto durationPredicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = durationPredicate;
+
+ int64_t metricId = 123456;
+ auto durationMetric = config.add_duration_metric();
+ durationMetric->set_id(metricId);
+ durationMetric->set_what(durationPredicate.id());
+ durationMetric->set_bucket(FIVE_MINUTES);
+ durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
+
+ auto metric_activation1 = config.add_metric_activation();
+ metric_activation1->set_metric_id(metricId);
+ auto event_activation1 = metric_activation1->add_event_activation();
+ event_activation1->set_atom_matcher_id(crashMatcher.id());
+ event_activation1->set_ttl_seconds(30); // 30 secs.
+
+ const int64_t bucketStartTimeNs = 10000000000;
+ const int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000LL * 1000LL;
+
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ sp<UidMap> m = new UidMap();
+ sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+ sp<AlarmMonitor> anomalyAlarmMonitor;
+ sp<AlarmMonitor> subscriberAlarmMonitor;
+ vector<int64_t> activeConfigsBroadcast;
+
+ int broadcastCount = 0;
+ StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor,
+ bucketStartTimeNs, [](const ConfigKey& key) { return true; },
+ [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
+ const vector<int64_t>& activeConfigs) {
+ broadcastCount++;
+ EXPECT_EQ(broadcastUid, uid);
+ activeConfigsBroadcast.clear();
+ activeConfigsBroadcast.insert(activeConfigsBroadcast.end(),
+ activeConfigs.begin(), activeConfigs.end());
+ return true;
+ });
+
+ processor.OnConfigUpdated(bucketStartTimeNs, cfgKey, config); // 0:00
+
+ EXPECT_EQ(processor.mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor.mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ auto& eventActivationMap = metricProducer->mEventActivationMap;
+
+ EXPECT_FALSE(metricsManager->isActive());
+ EXPECT_FALSE(metricProducer->mIsActive);
+ EXPECT_EQ(eventActivationMap.size(), 1u);
+ EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, 0);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+ std::unique_ptr<LogEvent> event;
+
+ // Turn screen off.
+ event = CreateScreenStateChangedEvent(
+ android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 2 * NS_PER_SEC); // 0:02
+ processor.OnLogEvent(event.get());
+
+ // Turn screen on.
+ const int64_t durationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:05
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs);
+ processor.OnLogEvent(event.get());
+
+ // Activate metric.
+ const int64_t activationStartNs = bucketStartTimeNs + 5 * NS_PER_SEC; // 0:10
+ const int64_t activationEndNs =
+ activationStartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 0:40
+ event = CreateAppCrashEvent(111, activationStartNs);
+ processor.OnLogEvent(event.get());
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_EQ(broadcastCount, 1);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+ // Expire activation.
+ const int64_t expirationNs = activationEndNs + 7 * NS_PER_SEC;
+ event = CreateScreenBrightnessChangedEvent(64, expirationNs); // 0:47
+ processor.OnLogEvent(event.get());
+ EXPECT_FALSE(metricsManager->isActive());
+ EXPECT_FALSE(metricProducer->mIsActive);
+ EXPECT_EQ(broadcastCount, 2);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 0);
+ EXPECT_EQ(eventActivationMap.size(), 1u);
+ EXPECT_TRUE(eventActivationMap.find(2) != eventActivationMap.end());
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, activationStartNs);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+ // Turn off screen 10 seconds after activation expiration.
+ const int64_t durationEndNs = activationEndNs + 10 * NS_PER_SEC; // 0:50
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, durationEndNs);
+ processor.OnLogEvent(event.get());
+
+ // Turn screen on.
+ const int64_t duration2StartNs = durationEndNs + 5 * NS_PER_SEC; // 0:55
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, duration2StartNs);
+ processor.OnLogEvent(event.get());
+
+ // Turn off screen.
+ const int64_t duration2EndNs = duration2StartNs + 10 * NS_PER_SEC; // 1:05
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF, duration2EndNs);
+ processor.OnLogEvent(event.get());
+
+ // Activate metric.
+ const int64_t activation2StartNs = duration2EndNs + 5 * NS_PER_SEC; // 1:10
+ const int64_t activation2EndNs =
+ activation2StartNs + event_activation1->ttl_seconds() * NS_PER_SEC; // 1:40
+ event = CreateAppCrashEvent(211, activation2StartNs);
+ processor.OnLogEvent(event.get());
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_EQ(broadcastCount, 3);
+ EXPECT_EQ(activeConfigsBroadcast.size(), 1);
+ EXPECT_EQ(activeConfigsBroadcast[0], cfgId);
+ EXPECT_EQ(eventActivationMap[2]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[2]->start_ns, activation2StartNs);
+ EXPECT_EQ(eventActivationMap[2]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+ ConfigMetricsReportList reports;
+ vector<uint8_t> buffer;
+ processor.onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1 * NS_PER_SEC, false, true,
+ ADB_DUMP, FAST, &buffer); // 5:01
+ EXPECT_TRUE(buffer.size() > 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStartEndTimestamp(&reports);
+ EXPECT_EQ(1, reports.reports_size());
+ EXPECT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_EQ(metricId, reports.reports(0).metrics(0).metric_id());
+ EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
+
+ const StatsLogReport::DurationMetricDataWrapper& durationMetrics =
+ reports.reports(0).metrics(0).duration_metrics();
+ EXPECT_EQ(1, durationMetrics.data_size());
+
+ auto data = durationMetrics.data(0);
+ EXPECT_EQ(1, data.bucket_info_size());
+
+ auto bucketInfo = data.bucket_info(0);
+ EXPECT_EQ(0, bucketInfo.bucket_num());
+ EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
+ EXPECT_EQ(expirationNs, bucketInfo.end_bucket_elapsed_nanos());
+ EXPECT_EQ(expirationNs - durationStartNs, bucketInfo.duration_nanos());
+}
+
+TEST(DurationMetricE2eTest, TestWithCondition) {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+ *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+
+ auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+ *config.add_predicate() = holdingWakelockPredicate;
+
+ auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
+ *config.add_predicate() = isInBackgroundPredicate;
+
+ auto durationMetric = config.add_duration_metric();
+ durationMetric->set_id(StringToId("WakelockDuration"));
+ durationMetric->set_what(holdingWakelockPredicate.id());
+ durationMetric->set_condition(isInBackgroundPredicate.id());
+ durationMetric->set_aggregation_type(DurationMetric::SUM);
+ durationMetric->set_bucket(FIVE_MINUTES);
+
+ ConfigKey cfgKey;
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ auto& eventActivationMap = metricProducer->mEventActivationMap;
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_TRUE(eventActivationMap.empty());
+
+ int appUid = 123;
+ std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")};
+
+ auto event = CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 10 * NS_PER_SEC); // 0:10
+ processor->OnLogEvent(event.get());
+
+ event = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 22 * NS_PER_SEC); // 0:22
+ processor->OnLogEvent(event.get());
+
+ event = CreateMoveToForegroundEvent(
+ appUid, bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC); // 3:15
+ processor->OnLogEvent(event.get());
+
+ event = CreateReleaseWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 4 * 60 * NS_PER_SEC); // 4:00
+ processor->OnLogEvent(event.get());
+
+ vector<uint8_t> buffer;
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true,
+ ADB_DUMP, FAST, &buffer);
+ EXPECT_GT(buffer.size(), 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+
+ EXPECT_EQ(1, reports.reports_size());
+ EXPECT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size());
+
+ auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
+
+ // Validate bucket info.
+ EXPECT_EQ(1, data.bucket_info_size());
+
+ auto bucketInfo = data.bucket_info(0);
+ EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
+ EXPECT_EQ((2 * 60 + 53) * NS_PER_SEC, bucketInfo.duration_nanos());
+}
+
+TEST(DurationMetricE2eTest, TestWithSlicedCondition) {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+ *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+
+ auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+ // The predicate is dimensioning by first attribution node by uid.
+ FieldMatcher dimensions = CreateAttributionUidDimensions(
+ android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
+ *config.add_predicate() = holdingWakelockPredicate;
+
+ auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
+ *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
+ CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
+ *config.add_predicate() = isInBackgroundPredicate;
+
+ auto durationMetric = config.add_duration_metric();
+ durationMetric->set_id(StringToId("WakelockDuration"));
+ durationMetric->set_what(holdingWakelockPredicate.id());
+ durationMetric->set_condition(isInBackgroundPredicate.id());
+ durationMetric->set_aggregation_type(DurationMetric::SUM);
+ // The metric is dimensioning by first attribution node and only by uid.
+ *durationMetric->mutable_dimensions_in_what() =
+ CreateAttributionUidDimensions(
+ android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ durationMetric->set_bucket(FIVE_MINUTES);
+
+ // Links between wakelock state atom and condition of app is in background.
+ auto links = durationMetric->add_links();
+ links->set_condition(isInBackgroundPredicate.id());
+ auto dimensionWhat = links->mutable_fields_in_what();
+ dimensionWhat->set_field(android::util::WAKELOCK_STATE_CHANGED);
+ dimensionWhat->add_child()->set_field(1); // uid field.
+ *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+ android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, { Position::FIRST });
+
+ ConfigKey cfgKey;
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ auto& eventActivationMap = metricProducer->mEventActivationMap;
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_TRUE(eventActivationMap.empty());
+
+ int appUid = 123;
+ std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")};
+
+ auto event = CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 10 * NS_PER_SEC); // 0:10
+ processor->OnLogEvent(event.get());
+
+ event = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 22 * NS_PER_SEC); // 0:22
+ processor->OnLogEvent(event.get());
+
+ event = CreateReleaseWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 60 * NS_PER_SEC); // 1:00
+ processor->OnLogEvent(event.get());
+
+
+ event = CreateMoveToForegroundEvent(
+ appUid, bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC); // 3:15
+ processor->OnLogEvent(event.get());
+
+ vector<uint8_t> buffer;
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true,
+ ADB_DUMP, FAST, &buffer);
+ EXPECT_GT(buffer.size(), 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+
+ EXPECT_EQ(1, reports.reports_size());
+ EXPECT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size());
+
+ auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
+ // Validate dimension value.
+ ValidateAttributionUidDimension(data.dimensions_in_what(),
+ android::util::WAKELOCK_STATE_CHANGED, appUid);
+ // Validate bucket info.
+ EXPECT_EQ(1, data.bucket_info_size());
+
+ auto bucketInfo = data.bucket_info(0);
+ EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
+ EXPECT_EQ(38 * NS_PER_SEC, bucketInfo.duration_nanos());
+}
+
+TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition) {
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+ auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+ *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+ *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+ *config.add_atom_matcher() = screenOnMatcher;
+
+ auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+ // The predicate is dimensioning by first attribution node by uid.
+ FieldMatcher dimensions = CreateAttributionUidDimensions(
+ android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
+ *config.add_predicate() = holdingWakelockPredicate;
+
+ auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
+ *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
+ CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
+ *config.add_predicate() = isInBackgroundPredicate;
+
+ auto durationMetric = config.add_duration_metric();
+ durationMetric->set_id(StringToId("WakelockDuration"));
+ durationMetric->set_what(holdingWakelockPredicate.id());
+ durationMetric->set_condition(isInBackgroundPredicate.id());
+ durationMetric->set_aggregation_type(DurationMetric::SUM);
+ // The metric is dimensioning by first attribution node and only by uid.
+ *durationMetric->mutable_dimensions_in_what() =
+ CreateAttributionUidDimensions(
+ android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+ durationMetric->set_bucket(FIVE_MINUTES);
+
+ // Links between wakelock state atom and condition of app is in background.
+ auto links = durationMetric->add_links();
+ links->set_condition(isInBackgroundPredicate.id());
+ auto dimensionWhat = links->mutable_fields_in_what();
+ dimensionWhat->set_field(android::util::WAKELOCK_STATE_CHANGED);
+ dimensionWhat->add_child()->set_field(1); // uid field.
+ *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+ android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, { Position::FIRST });
+
+ auto metric_activation1 = config.add_metric_activation();
+ metric_activation1->set_metric_id(durationMetric->id());
+ auto event_activation1 = metric_activation1->add_event_activation();
+ event_activation1->set_atom_matcher_id(screenOnMatcher.id());
+ event_activation1->set_ttl_seconds(60 * 2); // 2 minutes.
+
+ ConfigKey cfgKey;
+ uint64_t bucketStartTimeNs = 10000000000;
+ uint64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+ auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ auto& eventActivationMap = metricProducer->mEventActivationMap;
+ EXPECT_FALSE(metricsManager->isActive());
+ EXPECT_FALSE(metricProducer->mIsActive);
+ EXPECT_EQ(eventActivationMap.size(), 1u);
+ EXPECT_TRUE(eventActivationMap.find(4) != eventActivationMap.end());
+ EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[4]->start_ns, 0);
+ EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+ int appUid = 123;
+ std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(appUid, "App1")};
+
+ auto event = CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + 10 * NS_PER_SEC); // 0:10
+ processor->OnLogEvent(event.get());
+ EXPECT_FALSE(metricsManager->isActive());
+ EXPECT_FALSE(metricProducer->mIsActive);
+ EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[4]->start_ns, 0);
+ EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+ event = CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 22 * NS_PER_SEC); // 0:22
+ processor->OnLogEvent(event.get());
+ EXPECT_FALSE(metricsManager->isActive());
+ EXPECT_FALSE(metricProducer->mIsActive);
+ EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[4]->start_ns, 0);
+ EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+ const int64_t durationStartNs = bucketStartTimeNs + 30 * NS_PER_SEC; // 0:30
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, durationStartNs);
+ processor->OnLogEvent(event.get());
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs);
+ EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+ const int64_t durationEndNs =
+ durationStartNs + (event_activation1->ttl_seconds() + 30) * NS_PER_SEC; // 3:00
+ event = CreateAppCrashEvent(333, durationEndNs);
+ processor->OnLogEvent(event.get());
+ EXPECT_FALSE(metricsManager->isActive());
+ EXPECT_FALSE(metricProducer->mIsActive);
+ EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kNotActive);
+ EXPECT_EQ(eventActivationMap[4]->start_ns, durationStartNs);
+ EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+ event = CreateMoveToForegroundEvent(
+ appUid, bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC); // 3:15
+ processor->OnLogEvent(event.get());
+
+ event = CreateReleaseWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + (4 * 60 + 17) * NS_PER_SEC); // 4:17
+ processor->OnLogEvent(event.get());
+
+ event = CreateMoveToBackgroundEvent(
+ appUid, bucketStartTimeNs + (4 * 60 + 20) * NS_PER_SEC); // 4:20
+ processor->OnLogEvent(event.get());
+
+ event = CreateAcquireWakelockEvent(
+ attributions1, "wl1", bucketStartTimeNs + (4 * 60 + 25) * NS_PER_SEC); // 4:25
+ processor->OnLogEvent(event.get());
+
+ const int64_t duration2StartNs = bucketStartTimeNs + (4 * 60 + 30) * NS_PER_SEC; // 4:30
+ event = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON, duration2StartNs);
+ processor->OnLogEvent(event.get());
+ EXPECT_TRUE(metricsManager->isActive());
+ EXPECT_TRUE(metricProducer->mIsActive);
+ EXPECT_EQ(eventActivationMap[4]->state, ActivationState::kActive);
+ EXPECT_EQ(eventActivationMap[4]->start_ns, duration2StartNs);
+ EXPECT_EQ(eventActivationMap[4]->ttl_ns, event_activation1->ttl_seconds() * NS_PER_SEC);
+
+ vector<uint8_t> buffer;
+ ConfigMetricsReportList reports;
+ processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true,
+ ADB_DUMP, FAST, &buffer);
+ EXPECT_GT(buffer.size(), 0);
+ EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+ backfillDimensionPath(&reports);
+ backfillStringInReport(&reports);
+ backfillStartEndTimestamp(&reports);
+
+ EXPECT_EQ(1, reports.reports_size());
+ EXPECT_EQ(1, reports.reports(0).metrics_size());
+ EXPECT_EQ(1, reports.reports(0).metrics(0).duration_metrics().data_size());
+
+ auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
+ // Validate dimension value.
+ ValidateAttributionUidDimension(data.dimensions_in_what(),
+ android::util::WAKELOCK_STATE_CHANGED, appUid);
+ // Validate bucket info.
+ EXPECT_EQ(2, data.bucket_info_size());
+
+ auto bucketInfo = data.bucket_info(0);
+ EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
+ EXPECT_EQ(durationEndNs, bucketInfo.end_bucket_elapsed_nanos());
+ EXPECT_EQ(durationEndNs - durationStartNs, bucketInfo.duration_nanos());
+
+ bucketInfo = data.bucket_info(1);
+ EXPECT_EQ(durationEndNs, bucketInfo.start_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
+ EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - duration2StartNs, bucketInfo.duration_nanos());
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index 6ec0a11..c7ba9be 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -446,22 +446,23 @@
// Pulling alarm arrives on time and reset the sequential pulling alarm.
// Event should not be kept.
- processor->informPullAlarmFired(nextPullTimeNs + 1);
+ processor->informPullAlarmFired(nextPullTimeNs + 1); // 15 mins + 1 ns.
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs);
EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
+ // Activate the metric. A pull occurs upon activation.
const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis.
auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs);
- processor->OnLogEvent(batterySaverOnEvent.get());
+ processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms.
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
- // This event should be kept. 1 total.
- processor->informPullAlarmFired(nextPullTimeNs + 1);
+ // This event should be kept. 2 total.
+ processor->informPullAlarmFired(nextPullTimeNs + 1); // 20 mins + 1 ns.
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs,
nextPullTimeNs);
- // This event should be kept. 2 total.
- processor->informPullAlarmFired(nextPullTimeNs + 2);
+ // This event should be kept. 3 total.
+ processor->informPullAlarmFired(nextPullTimeNs + 2); // 25 mins + 2 ns.
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs);
// Create random event to deactivate metric.
@@ -469,7 +470,7 @@
processor->OnLogEvent(deactivationEvent.get());
EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
- // Event should not be kept. 2 total.
+ // Event should not be kept. 3 total.
processor->informPullAlarmFired(nextPullTimeNs + 3);
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs);
@@ -497,27 +498,39 @@
EXPECT_EQ(1 /* subsystem name field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
- EXPECT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(3, data.bucket_info_size());
- EXPECT_EQ(1, data.bucket_info(0).atom_size());
- EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, data.bucket_info(0).elapsed_timestamp_nanos(0));
- EXPECT_EQ(0, data.bucket_info(0).wall_clock_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(0).atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(data.bucket_info(0).atom(0).subsystem_sleep_state().time_millis(), 0);
+ auto bucketInfo = data.bucket_info(0);
+ EXPECT_EQ(1, bucketInfo.atom_size());
+ EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size());
+ EXPECT_EQ(activationNs, bucketInfo.elapsed_timestamp_nanos(0));
+ EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
+ EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty());
+ EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0);
- EXPECT_EQ(1, data.bucket_info(1).atom_size());
- EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
- EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 2, data.bucket_info(1).elapsed_timestamp_nanos(0));
- EXPECT_EQ(0, data.bucket_info(1).wall_clock_timestamp_nanos_size());
+ bucketInfo = data.bucket_info(1);
+ EXPECT_EQ(1, bucketInfo.atom_size());
+ EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1, bucketInfo.elapsed_timestamp_nanos(0));
+ EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
+ EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty());
+ EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0);
+
+ bucketInfo = data.bucket_info(2);
+ EXPECT_EQ(1, bucketInfo.atom_size());
+ EXPECT_EQ(1, bucketInfo.elapsed_timestamp_nanos_size());
+ EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 2, bucketInfo.elapsed_timestamp_nanos(0));
+ EXPECT_EQ(0, bucketInfo.wall_clock_timestamp_nanos_size());
EXPECT_EQ(MillisToNano(NanoToMillis(baseTimeNs + 5 * bucketSizeNs)),
- data.bucket_info(1).start_bucket_elapsed_nanos());
+ bucketInfo.start_bucket_elapsed_nanos());
EXPECT_EQ(MillisToNano(NanoToMillis(activationNs + ttlNs + 1)),
- data.bucket_info(1).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(1).atom(0).subsystem_sleep_state().subsystem_name().empty());
- EXPECT_GT(data.bucket_info(1).atom(0).subsystem_sleep_state().time_millis(), 0);
+ bucketInfo.end_bucket_elapsed_nanos());
+ EXPECT_TRUE(bucketInfo.atom(0).subsystem_sleep_state().subsystem_name().empty());
+ EXPECT_GT(bucketInfo.atom(0).subsystem_sleep_state().time_millis(), 0);
}
TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsNoCondition) {
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index ff6af38..fb878dc7 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -306,18 +306,20 @@
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
// Pulling alarm arrives on time and reset the sequential pulling alarm.
- processor->informPullAlarmFired(expectedPullTimeNs + 1);
+ processor->informPullAlarmFired(expectedPullTimeNs + 1); // 15 mins + 1 ns.
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs);
+ EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
+ // Activate the metric. A pull occurs here
const int64_t activationNs = configAddedTimeNs + bucketSizeNs + (2 * 1000 * 1000); // 2 millis.
auto batterySaverOnEvent = CreateBatterySaverOnEvent(activationNs);
- processor->OnLogEvent(batterySaverOnEvent.get());
+ processor->OnLogEvent(batterySaverOnEvent.get()); // 15 mins + 2 ms.
EXPECT_TRUE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
- processor->informPullAlarmFired(expectedPullTimeNs + 1);
+ processor->informPullAlarmFired(expectedPullTimeNs + 1); // 20 mins + 1 ns.
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, expectedPullTimeNs);
- processor->informPullAlarmFired(expectedPullTimeNs + 1);
+ processor->informPullAlarmFired(expectedPullTimeNs + 2); // 25 mins + 2 ns.
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs);
// Create random event to deactivate metric.
@@ -325,10 +327,11 @@
processor->OnLogEvent(deactivationEvent.get());
EXPECT_FALSE(processor->mMetricsManagers.begin()->second->mAllMetricProducers[0]->isActive());
- processor->informPullAlarmFired(expectedPullTimeNs + 1);
+ processor->informPullAlarmFired(expectedPullTimeNs + 3);
EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, expectedPullTimeNs);
- processor->informPullAlarmFired(expectedPullTimeNs + 1);
+ processor->informPullAlarmFired(expectedPullTimeNs + 4);
+ EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs);
ConfigMetricsReportList reports;
vector<uint8_t> buffer;
@@ -352,12 +355,18 @@
EXPECT_EQ(1 /* subsystem name field */,
data.dimensions_in_what().value_tuple().dimensions_value(0).field());
EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
- // We have 1 full bucket, the two surrounding the activation are dropped.
- EXPECT_EQ(1, data.bucket_info_size());
+ // We have 2 full buckets, the two surrounding the activation are dropped.
+ EXPECT_EQ(2, data.bucket_info_size());
- EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
- EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_EQ(1, data.bucket_info(0).values_size());
+ auto bucketInfo = data.bucket_info(0);
+ EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
+ EXPECT_EQ(1, bucketInfo.values_size());
+
+ bucketInfo = data.bucket_info(1);
+ EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, bucketInfo.start_bucket_elapsed_nanos());
+ EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
+ EXPECT_EQ(1, bucketInfo.values_size());
}
#else
diff --git a/cmds/statsd/tests/storage/StorageManager_test.cpp b/cmds/statsd/tests/storage/StorageManager_test.cpp
index cae2f30..9e15e99 100644
--- a/cmds/statsd/tests/storage/StorageManager_test.cpp
+++ b/cmds/statsd/tests/storage/StorageManager_test.cpp
@@ -125,6 +125,105 @@
EXPECT_EQ("300_2000_123454_history", list[3].mFileName);
}
+const string testDir = "/data/misc/stats-data/";
+const string file1 = testDir + "2557169347_1066_1";
+const string file2 = testDir + "2557169349_1066_1";
+const string file1_history = file1 + "_history";
+const string file2_history = file2 + "_history";
+
+bool prepareLocalHistoryTestFiles() {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(
+ open(file1.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR)));
+ if (fd != -1) {
+ dprintf(fd, "content");
+ } else {
+ return false;
+ }
+
+ android::base::unique_fd fd2(TEMP_FAILURE_RETRY(
+ open(file2.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR)));
+ if (fd2 != -1) {
+ dprintf(fd2, "content");
+ } else {
+ return false;
+ }
+ return true;
+}
+
+void clearLocalHistoryTestFiles() {
+ TEMP_FAILURE_RETRY(remove(file1.c_str()));
+ TEMP_FAILURE_RETRY(remove(file2.c_str()));
+ TEMP_FAILURE_RETRY(remove(file1_history.c_str()));
+ TEMP_FAILURE_RETRY(remove(file2_history.c_str()));
+}
+
+bool fileExist(string name) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(name.c_str(), O_RDONLY | O_CLOEXEC)));
+ return fd != -1;
+}
+
+/* The following AppendConfigReportTests test the 4 combinations of [whether erase data] [whether
+ * the caller is adb] */
+TEST(StorageManagerTest, AppendConfigReportTest1) {
+ EXPECT_TRUE(prepareLocalHistoryTestFiles());
+
+ ProtoOutputStream out;
+ StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, false /*erase?*/,
+ false /*isAdb?*/);
+
+ EXPECT_FALSE(fileExist(file1));
+ EXPECT_FALSE(fileExist(file2));
+
+ EXPECT_TRUE(fileExist(file1_history));
+ EXPECT_TRUE(fileExist(file2_history));
+ clearLocalHistoryTestFiles();
+}
+
+TEST(StorageManagerTest, AppendConfigReportTest2) {
+ EXPECT_TRUE(prepareLocalHistoryTestFiles());
+
+ ProtoOutputStream out;
+ StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, true /*erase?*/,
+ false /*isAdb?*/);
+
+ EXPECT_FALSE(fileExist(file1));
+ EXPECT_FALSE(fileExist(file2));
+ EXPECT_FALSE(fileExist(file1_history));
+ EXPECT_FALSE(fileExist(file2_history));
+
+ clearLocalHistoryTestFiles();
+}
+
+TEST(StorageManagerTest, AppendConfigReportTest3) {
+ EXPECT_TRUE(prepareLocalHistoryTestFiles());
+
+ ProtoOutputStream out;
+ StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, false /*erase?*/,
+ true /*isAdb?*/);
+
+ EXPECT_TRUE(fileExist(file1));
+ EXPECT_TRUE(fileExist(file2));
+ EXPECT_FALSE(fileExist(file1_history));
+ EXPECT_FALSE(fileExist(file2_history));
+
+ clearLocalHistoryTestFiles();
+}
+
+TEST(StorageManagerTest, AppendConfigReportTest4) {
+ EXPECT_TRUE(prepareLocalHistoryTestFiles());
+
+ ProtoOutputStream out;
+ StorageManager::appendConfigMetricsReport(ConfigKey(1066, 1), &out, true /*erase?*/,
+ true /*isAdb?*/);
+
+ EXPECT_FALSE(fileExist(file1));
+ EXPECT_FALSE(fileExist(file2));
+ EXPECT_FALSE(fileExist(file1_history));
+ EXPECT_FALSE(fileExist(file2_history));
+
+ clearLocalHistoryTestFiles();
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 500a04f..46a520a 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -278,7 +278,7 @@
String resolvedType, boolean fgRequired, String callingPackage, int userId,
boolean allowBackgroundActivityStarts) throws TransactionTooLargeException;
- public abstract void disconnectActivityFromServices(Object connectionHolder);
+ public abstract void disconnectActivityFromServices(Object connectionHolder, Object conns);
public abstract void cleanUpServices(int userId, ComponentName component, Intent baseIntent);
public abstract ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId);
public abstract void ensureBootCompleted();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1e982bc..3a74f7d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5564,15 +5564,9 @@
private void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
- int configDiff = 0;
+ int configDiff;
+ boolean equivalent;
- // This flag tracks whether the new configuration is fundamentally equivalent to the
- // existing configuration. This is necessary to determine whether non-activity
- // callbacks should receive notice when the only changes are related to non-public fields.
- // We do not gate calling {@link #performActivityConfigurationChanged} based on this flag
- // as that method uses the same check on the activity config override as well.
- final boolean equivalent = config != null && mConfiguration != null
- && (0 == mConfiguration.diffPublicOnly(config));
final Theme systemTheme = getSystemContext().getTheme();
final Theme systemUiTheme = getSystemUiContext().getTheme();
@@ -5590,6 +5584,13 @@
return;
}
+ // This flag tracks whether the new configuration is fundamentally equivalent to the
+ // existing configuration. This is necessary to determine whether non-activity callbacks
+ // should receive notice when the only changes are related to non-public fields.
+ // We do not gate calling {@link #performActivityConfigurationChanged} based on this
+ // flag as that method uses the same check on the activity config override as well.
+ equivalent = mConfiguration != null && (0 == mConfiguration.diffPublicOnly(config));
+
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle configuration changed: "
+ config);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index cb939f0..e08f4a2 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -727,6 +727,7 @@
INSTALL_ENABLE_ROLLBACK,
INSTALL_ALLOW_DOWNGRADE,
INSTALL_STAGED,
+ INSTALL_DRY_RUN,
})
@Retention(RetentionPolicy.SOURCE)
public @interface InstallFlags {}
@@ -904,6 +905,14 @@
*/
public static final int INSTALL_STAGED = 0x00200000;
+ /**
+ * Flag parameter for {@link #installPackage} to indicate that package should only be verified
+ * but not installed.
+ *
+ * @hide
+ */
+ public static final int INSTALL_DRY_RUN = 0x00800000;
+
/** @hide */
@IntDef(flag = true, prefix = { "DONT_KILL_APP" }, value = {
DONT_KILL_APP
diff --git a/core/java/android/content/rollback/IRollbackManager.aidl b/core/java/android/content/rollback/IRollbackManager.aidl
index db9fcb6..1b84f29 100644
--- a/core/java/android/content/rollback/IRollbackManager.aidl
+++ b/core/java/android/content/rollback/IRollbackManager.aidl
@@ -30,9 +30,9 @@
String callerPackageName, in IntentSender statusReceiver);
// Exposed for use from the system server only. Callback from the package
- // manager during the install flow when user data can be restored for a given
+ // manager during the install flow when user data can be backed up and restored for a given
// package.
- void restoreUserData(String packageName, in int[] userIds, int appId, long ceDataInode,
+ void snapshotAndRestoreUserData(String packageName, in int[] userIds, int appId, long ceDataInode,
String seInfo, int token);
// Exposed for test purposes only.
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index f662b61..c8276b2 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -857,8 +857,10 @@
*
* @param cameraId a non-{@code null} camera identifier
* @return {@code true} if cameraId is a hidden physical camera device
+ *
+ * @hide
*/
- private boolean isHiddenPhysicalCamera(String cameraId) {
+ public static boolean isHiddenPhysicalCamera(String cameraId) {
try {
ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
// If no camera service, no support
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index d44b6b5..6eaf54b 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -28,6 +28,7 @@
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraCharacteristics.Key;
import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.utils.HashCodeHelpers;
@@ -661,6 +662,7 @@
private List<Integer> mCapabilities;
private int mHwLevel, mCameraId;
private StreamConfigurationMap mStreamConfigMap;
+ private boolean mIsHiddenPhysicalCamera;
private final Size kPreviewSizeBound = new Size(1920, 1088);
@@ -680,6 +682,8 @@
mCapabilities = capabilities;
mStreamConfigMap = sm;
mHwLevel = hwLevel;
+ mIsHiddenPhysicalCamera =
+ CameraManager.isHiddenPhysicalCamera(Integer.toString(mCameraId));
}
/**
@@ -893,8 +897,10 @@
Size recordingMaxSize = new Size(0, 0);
Size previewMaxSize = new Size(0, 0);
Size vgaSize = new Size(640, 480);
- if (isExternalCamera()) {
- recordingMaxSize = getMaxExternalRecordingSize();
+ // For external camera, or hidden physical camera, CamcorderProfile may not be
+ // available, so get maximum recording size using stream configuration map.
+ if (isExternalCamera() || mIsHiddenPhysicalCamera) {
+ recordingMaxSize = getMaxCameraRecordingSize();
} else {
recordingMaxSize = getMaxRecordingSize();
}
@@ -1123,12 +1129,12 @@
}
/**
- * Return the maximum supported video size for external cameras using data from
+ * Return the maximum supported video size for cameras using data from
* the stream configuration map.
*
* @return Maximum supported video size.
*/
- private @NonNull Size getMaxExternalRecordingSize() {
+ private @NonNull Size getMaxCameraRecordingSize() {
final Size FULLHD = new Size(1920, 1080);
Size[] videoSizeArr = mStreamConfigMap.getOutputSizes(
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 1b41694..8938ddd 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -32,6 +32,8 @@
import android.content.pm.PackageManager;
import android.os.storage.IStorageManager;
import android.provider.Settings;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
import android.telephony.euicc.EuiccManager;
import android.text.TextUtils;
import android.text.format.DateFormat;
@@ -59,10 +61,12 @@
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
+import java.util.List;
import java.util.Locale;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
@@ -90,11 +94,14 @@
private static final long PUBLISH_PROGRESS_INTERVAL_MS = 500;
private static final long DEFAULT_EUICC_FACTORY_RESET_TIMEOUT_MILLIS = 30000L; // 30 s
-
private static final long MIN_EUICC_FACTORY_RESET_TIMEOUT_MILLIS = 5000L; // 5 s
-
private static final long MAX_EUICC_FACTORY_RESET_TIMEOUT_MILLIS = 60000L; // 60 s
+ private static final long DEFAULT_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS =
+ 45000L; // 45 s
+ private static final long MIN_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS = 15000L; // 15 s
+ private static final long MAX_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS = 90000L; // 90 s
+
/** Used to communicate with recovery. See bootable/recovery/recovery.cpp. */
private static final File RECOVERY_DIR = new File("/cache/recovery");
private static final File LOG_FILE = new File(RECOVERY_DIR, "log");
@@ -102,9 +109,14 @@
private static final String LAST_PREFIX = "last_";
private static final String ACTION_EUICC_FACTORY_RESET =
"com.android.internal.action.EUICC_FACTORY_RESET";
+ private static final String ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS =
+ "com.android.internal.action.EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS";
- /** used in {@link #wipeEuiccData} as package name of callback intent */
- private static final String PACKAGE_NAME_WIPING_EUICC_DATA_CALLBACK = "android";
+ /**
+ * Used in {@link #wipeEuiccData} & {@link #removeEuiccInvisibleSubs} as package name of
+ * callback intent.
+ */
+ private static final String PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK = "android";
/**
* The recovery image uses this file to identify the location (i.e. blocks)
@@ -757,8 +769,11 @@
// Block until the ordered broadcast has completed.
condition.block();
+ EuiccManager euiccManager = context.getSystemService(EuiccManager.class);
if (wipeEuicc) {
- wipeEuiccData(context, PACKAGE_NAME_WIPING_EUICC_DATA_CALLBACK);
+ wipeEuiccData(context, PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK);
+ } else {
+ removeEuiccInvisibleSubs(context, euiccManager);
}
String shutdownArg = null;
@@ -854,6 +869,110 @@
return false;
}
+ private static void removeEuiccInvisibleSubs(
+ Context context, EuiccManager euiccManager) {
+ ContentResolver cr = context.getContentResolver();
+ if (Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) == 0) {
+ // If the eUICC isn't provisioned, there's no need to remove euicc invisible profiles,
+ // as there's nothing to be removed.
+ Log.i(TAG, "Skip removing eUICC invisible profiles as it is not provisioned.");
+ return;
+ } else if (euiccManager == null || !euiccManager.isEnabled()) {
+ Log.i(TAG, "Skip removing eUICC invisible profiles as eUICC manager is not available.");
+ return;
+ }
+ SubscriptionManager subscriptionManager =
+ context.getSystemService(SubscriptionManager.class);
+ List<SubscriptionInfo> availableSubs =
+ subscriptionManager.getAvailableSubscriptionInfoList();
+ if (availableSubs == null || availableSubs.isEmpty()) {
+ Log.i(TAG, "Skip removing eUICC invisible profiles as no available profiles found.");
+ return;
+ }
+ List<SubscriptionInfo> invisibleSubs = new ArrayList<>();
+ for (SubscriptionInfo sub : availableSubs) {
+ if (sub.isEmbedded() && !subscriptionManager.isSubscriptionVisible(sub)) {
+ invisibleSubs.add(sub);
+ }
+ }
+ removeEuiccInvisibleSubs(context, invisibleSubs, euiccManager);
+ }
+
+ private static boolean removeEuiccInvisibleSubs(
+ Context context, List<SubscriptionInfo> subscriptionInfos, EuiccManager euiccManager) {
+ if (subscriptionInfos == null || subscriptionInfos.isEmpty()) {
+ Log.i(TAG, "There are no eUICC invisible profiles needed to be removed.");
+ return true;
+ }
+ CountDownLatch removeSubsLatch = new CountDownLatch(subscriptionInfos.size());
+ final AtomicInteger removedSubsCount = new AtomicInteger(0);
+
+ BroadcastReceiver removeEuiccSubsReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS.equals(intent.getAction())) {
+ if (getResultCode() != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) {
+ int detailedCode = intent.getIntExtra(
+ EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0);
+ Log.e(TAG, "Error removing euicc opportunistic profile, Detailed code = "
+ + detailedCode);
+ } else {
+ Log.e(TAG, "Successfully remove euicc opportunistic profile.");
+ removedSubsCount.incrementAndGet();
+ }
+ removeSubsLatch.countDown();
+ }
+ }
+ };
+
+ Intent intent = new Intent(ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS);
+ intent.setPackage(PACKAGE_NAME_EUICC_DATA_MANAGEMENT_CALLBACK);
+ PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser(
+ context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT, UserHandle.SYSTEM);
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ACTION_EUICC_REMOVE_INVISIBLE_SUBSCRIPTIONS);
+ HandlerThread euiccHandlerThread =
+ new HandlerThread("euiccRemovingSubsReceiverThread");
+ euiccHandlerThread.start();
+ Handler euiccHandler = new Handler(euiccHandlerThread.getLooper());
+ context.getApplicationContext()
+ .registerReceiver(
+ removeEuiccSubsReceiver, intentFilter, null, euiccHandler);
+ for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
+ Log.i(
+ TAG,
+ "Remove invisible subscription " + subscriptionInfo.getSubscriptionId()
+ + " from card " + subscriptionInfo.getCardId());
+ euiccManager.createForCardId(subscriptionInfo.getCardId())
+ .deleteSubscription(subscriptionInfo.getSubscriptionId(), callbackIntent);
+ }
+ try {
+ long waitingTimeMillis = Settings.Global.getLong(
+ context.getContentResolver(),
+ Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS,
+ DEFAULT_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS);
+ if (waitingTimeMillis < MIN_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS) {
+ waitingTimeMillis = MIN_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS;
+ } else if (waitingTimeMillis > MAX_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS) {
+ waitingTimeMillis = MAX_EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS;
+ }
+ if (!removeSubsLatch.await(waitingTimeMillis, TimeUnit.MILLISECONDS)) {
+ Log.e(TAG, "Timeout removing invisible euicc profiles.");
+ return false;
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ Log.e(TAG, "Removing invisible euicc profiles interrupted", e);
+ return false;
+ } finally {
+ context.getApplicationContext().unregisterReceiver(removeEuiccSubsReceiver);
+ if (euiccHandlerThread != null) {
+ euiccHandlerThread.quit();
+ }
+ }
+ return removedSubsCount.get() == subscriptionInfos.size();
+ }
+
/** {@hide} */
public static void rebootPromptAndWipeUserData(Context context, String reason)
throws IOException {
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 3ea3bbc..d42478e5 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -407,7 +407,7 @@
*/
String msgStr = args.size() + "\n" + String.join("\n", args) + "\n";
- if (useUsapPool && mUsapPoolEnabled && isValidUsapCommand(args)) {
+ if (useUsapPool && mUsapPoolEnabled && canAttemptUsap(args)) {
try {
return attemptUsapSendArgsAndGetResult(zygoteState, msgStr);
} catch (IOException ex) {
@@ -498,13 +498,21 @@
* @param args Zygote/USAP command arguments
* @return True if the command can be passed to a USAP; false otherwise
*/
- private static boolean isValidUsapCommand(ArrayList<String> args) {
+ private static boolean canAttemptUsap(ArrayList<String> args) {
for (String flag : args) {
for (String badFlag : INVALID_USAP_FLAGS) {
if (flag.startsWith(badFlag)) {
return false;
}
}
+ if (flag.startsWith("--nice-name=")) {
+ // Check if the wrap property is set, usap would ignore it.
+ String niceName = flag.substring(12);
+ String property_value = SystemProperties.get("wrap." + niceName);
+ if (property_value != null && property_value.length() != 0) {
+ return false;
+ }
+ }
}
return true;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0db5c36..0271b88 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -13551,6 +13551,16 @@
"location_settings_link_to_permissions_enabled";
/**
+ * Flag to set the waiting time for removing invisible euicc profiles inside System >
+ * Settings.
+ * Type: long
+ *
+ * @hide
+ */
+ public static final String EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS =
+ "euicc_removing_invisible_profiles_timeout_millis";
+
+ /**
* Flag to set the waiting time for euicc factory reset inside System > Settings
* Type: long
*
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e071120..dfb7e89 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1546,6 +1546,7 @@
}
void setWindowStopped(boolean stopped) {
+ checkThread();
if (mStopped != stopped) {
mStopped = stopped;
final ThreadedRenderer renderer = mAttachInfo.mThreadedRenderer;
@@ -1567,7 +1568,7 @@
}
if (mStopped) {
- if (mSurfaceHolder != null) {
+ if (mSurfaceHolder != null && mSurface.isValid()) {
notifySurfaceDestroyed();
}
destroySurface();
@@ -2025,9 +2026,18 @@
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
+ } else if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
+ || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ // For wrap content, we have to remeasure later on anyways. Use size consistent with
+ // below so we get best use of the measure cache.
+ desiredWindowWidth = dipToPx(config.screenWidthDp);
+ desiredWindowHeight = dipToPx(config.screenHeightDp);
} else {
- desiredWindowWidth = mWinFrame.width();
- desiredWindowHeight = mWinFrame.height();
+ // After addToDisplay, the frame contains the frameHint from window manager, which
+ // for most windows is going to be the same size as the result of relayoutWindow.
+ // Using this here allows us to avoid remeasuring after relayoutWindow
+ desiredWindowWidth = frame.width();
+ desiredWindowHeight = frame.height();
}
// We used to use the following condition to choose 32 bits drawing caches:
@@ -2233,6 +2243,8 @@
final boolean isViewVisible = viewVisibility == View.VISIBLE;
final boolean windowRelayoutWasForced = mForceNextWindowRelayout;
+ boolean surfaceSizeChanged = false;
+
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
mForceNextWindowRelayout = false;
@@ -2311,7 +2323,7 @@
final boolean cutoutChanged = !mPendingDisplayCutout.equals(
mAttachInfo.mDisplayCutout);
final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets);
- final boolean surfaceSizeChanged = (relayoutResult
+ surfaceSizeChanged = (relayoutResult
& WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
surfaceChanged |= surfaceSizeChanged;
final boolean alwaysConsumeSystemBarsChanged =
@@ -2594,7 +2606,7 @@
maybeHandleWindowMove(frame);
}
- if (surfaceChanged) {
+ if (surfaceSizeChanged) {
updateBoundsSurface();
}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 8a111cf..379acbe 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -636,13 +636,21 @@
}
public void setStoppedState(IBinder token, boolean stopped) {
+ ArrayList<ViewRootImpl> nonCurrentThreadRoots = null;
synchronized (mLock) {
int count = mViews.size();
for (int i = count - 1; i >= 0; i--) {
if (token == null || mParams.get(i).token == token) {
ViewRootImpl root = mRoots.get(i);
// Client might remove the view by "stopped" event.
- root.setWindowStopped(stopped);
+ if (root.mThread == Thread.currentThread()) {
+ root.setWindowStopped(stopped);
+ } else {
+ if (nonCurrentThreadRoots == null) {
+ nonCurrentThreadRoots = new ArrayList<>();
+ }
+ nonCurrentThreadRoots.add(root);
+ }
// Recursively forward stopped state to View's attached
// to this Window rather than the root application token,
// e.g. PopupWindow's.
@@ -650,6 +658,16 @@
}
}
}
+
+ // Update the stopped state synchronously to ensure the surface won't be used after server
+ // side has destroyed it. This operation should be outside the lock to avoid any potential
+ // paths from setWindowStopped to WindowManagerGlobal which may cause deadlocks.
+ if (nonCurrentThreadRoots != null) {
+ for (int i = nonCurrentThreadRoots.size() - 1; i >= 0; i--) {
+ ViewRootImpl root = nonCurrentThreadRoots.get(i);
+ root.mHandler.runWithScissors(() -> root.setWindowStopped(stopped), 0);
+ }
+ }
}
public void reportNewConfiguration(Configuration config) {
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index b9ed963..7af45fc 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.app;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -64,8 +66,21 @@
String component = Settings.Secure.getString(getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT);
+
+ if (isGestureNavigateEnabled()) {
+ TextView promptPrologue = findViewById(R.id.accessibility_button_prompt_prologue);
+ promptPrologue.setText(isTouchExploreOn()
+ ? R.string.accessibility_gesture_3finger_prompt_text
+ : R.string.accessibility_gesture_prompt_text);
+ }
+
if (TextUtils.isEmpty(component)) {
TextView prompt = findViewById(R.id.accessibility_button_prompt);
+ if (isGestureNavigateEnabled()) {
+ prompt.setText(isTouchExploreOn()
+ ? R.string.accessibility_gesture_3finger_instructional_text
+ : R.string.accessibility_gesture_instructional_text);
+ }
prompt.setVisibility(View.VISIBLE);
}
@@ -91,6 +106,16 @@
});
}
+ private boolean isGestureNavigateEnabled() {
+ return NAV_BAR_MODE_GESTURAL == getResources().getInteger(
+ com.android.internal.R.integer.config_navBarInteractionMode);
+ }
+
+ private boolean isTouchExploreOn() {
+ return ((AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE))
+ .isTouchExplorationEnabled();
+ }
+
private static List<AccessibilityButtonTarget> getServiceAccessibilityButtonTargets(
@NonNull Context context) {
AccessibilityManager ams = (AccessibilityManager) context.getSystemService(
@@ -177,4 +202,4 @@
return mDrawable;
}
}
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index ee99837..dc3099d 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -716,18 +716,31 @@
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ adjustPreviewWidth(newConfig.orientation, null);
+ }
+
+ private boolean shouldDisplayLandscape(int orientation) {
+ // Sharesheet fixes the # of items per row and therefore can not correctly lay out
+ // when in the restricted size of multi-window mode. In the future, would be nice
+ // to use minimum dp size requirements instead
+ return orientation == Configuration.ORIENTATION_LANDSCAPE && !isInMultiWindowMode();
+ }
+
+ private void adjustPreviewWidth(int orientation, View parent) {
int width = -1;
- if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ if (shouldDisplayLandscape(orientation)) {
width = getResources().getDimensionPixelSize(R.dimen.chooser_preview_width);
}
- updateLayoutWidth(R.id.content_preview_text_layout, width);
- updateLayoutWidth(R.id.content_preview_title_layout, width);
- updateLayoutWidth(R.id.content_preview_file_layout, width);
+ parent = parent == null ? getWindow().getDecorView() : parent;
+
+ updateLayoutWidth(R.id.content_preview_text_layout, width, parent);
+ updateLayoutWidth(R.id.content_preview_title_layout, width, parent);
+ updateLayoutWidth(R.id.content_preview_file_layout, width, parent);
}
- private void updateLayoutWidth(int layoutResourceId, int width) {
- View view = findViewById(layoutResourceId);
+ private void updateLayoutWidth(int layoutResourceId, int width, View parent) {
+ View view = parent.findViewById(layoutResourceId);
if (view != null && view.getLayoutParams() != null) {
LayoutParams params = view.getLayoutParams();
params.width = width;
@@ -740,18 +753,27 @@
ViewGroup parent) {
if (convertView != null) return convertView;
+ ViewGroup layout = null;
+
switch (previewType) {
case CONTENT_PREVIEW_TEXT:
- return displayTextContentPreview(targetIntent, layoutInflater, parent);
+ layout = displayTextContentPreview(targetIntent, layoutInflater, parent);
+ break;
case CONTENT_PREVIEW_IMAGE:
- return displayImageContentPreview(targetIntent, layoutInflater, parent);
+ layout = displayImageContentPreview(targetIntent, layoutInflater, parent);
+ break;
case CONTENT_PREVIEW_FILE:
- return displayFileContentPreview(targetIntent, layoutInflater, parent);
+ layout = displayFileContentPreview(targetIntent, layoutInflater, parent);
+ break;
default:
Log.e(TAG, "Unexpected content preview type: " + previewType);
}
- return null;
+ if (layout != null) {
+ adjustPreviewWidth(getResources().getConfiguration().orientation, layout);
+ }
+
+ return layout;
}
private ViewGroup displayTextContentPreview(Intent targetIntent, LayoutInflater layoutInflater,
@@ -2180,9 +2202,7 @@
if (mChooserRowAdapter.consumeLayoutRequest()
|| mChooserRowAdapter.calculateChooserTargetWidth(availableWidth)
|| mAdapterView.getAdapter() == null) {
- if (mAdapterView.getAdapter() == null) {
- mAdapterView.setAdapter(mChooserRowAdapter);
- }
+ mAdapterView.setAdapter(mChooserRowAdapter);
getMainThreadHandler().post(() -> {
if (mResolverDrawerLayout == null || mChooserRowAdapter == null) {
@@ -2225,9 +2245,9 @@
}
}
- boolean isPortrait = getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_PORTRAIT;
- if (directShareHeight != 0 && isSendAction(getTargetIntent()) && isPortrait) {
+ boolean isExpandable = getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_PORTRAIT && !isInMultiWindowMode();
+ if (directShareHeight != 0 && isSendAction(getTargetIntent()) && isExpandable) {
// make sure to leave room for direct share 4->8 expansion
int requiredExpansionHeight =
(int) (directShareHeight / DIRECT_SHARE_EXPANSION_RATE);
@@ -2791,8 +2811,7 @@
private int getMaxTargetsPerRow() {
int maxTargets = MAX_TARGETS_PER_ROW_PORTRAIT;
- if (getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE) {
+ if (shouldDisplayLandscape(getResources().getConfiguration().orientation)) {
maxTargets = MAX_TARGETS_PER_ROW_LANDSCAPE;
}
@@ -3178,7 +3197,8 @@
int orientation = getResources().getConfiguration().orientation;
boolean canExpandDirectShare =
mChooserListAdapter.getNumShortcutResults() > getMaxTargetsPerRow()
- && orientation == Configuration.ORIENTATION_PORTRAIT;
+ && orientation == Configuration.ORIENTATION_PORTRAIT
+ && !isInMultiWindowMode();
if (mDirectShareViewHolder != null && canExpandDirectShare) {
mDirectShareViewHolder.handleScroll(mAdapterView, y, oldy, getMaxTargetsPerRow());
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 1b0a458..9b3fb0b 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -106,13 +106,6 @@
*/
public static final String HASH_SALT_MAX_DAYS = "hash_salt_max_days";
- // Flag related to Privacy Indicators
-
- /**
- * Whether the Permissions Hub is showing.
- */
- public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_enabled";
-
// Flags related to Assistant Handles
/**
@@ -132,6 +125,13 @@
*/
public static final String ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS =
"assist_handles_shown_frequency_threshold_ms";
+ // Flag related to clock face
+
+ /**
+ * (String) Contains the clock plugin service names that are not allow to be shown.
+ * Each service name is seperated by a comma(",") in the string.
+ */
+ public static final String CLOCK_FACE_BLACKLIST = "clock_face_blacklist";
/**
* (long) How long, in milliseconds, for teaching behaviors to wait before considering the user
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 24ad751..392f074 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -28,7 +28,6 @@
import java.io.File;
import java.io.FilenameFilter;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -138,11 +137,12 @@
if (!dedup.isEmpty()) {
mFileNumbers.addAll(dedup);
Collections.sort(mFileNumbers);
+ setActiveFile(mFileNumbers.get(mFileNumbers.size() - 1));
} else {
// No file found, default to have file 0.
mFileNumbers.add(0);
+ setActiveFile(0);
}
- createActiveFile();
}
/**
@@ -157,22 +157,15 @@
mHistoryBuffer = historyBuffer;
}
/**
- * The highest numbered history file is active file that mHistoryBuffer is backed up into.
- * If file does not exists, truncate() creates a empty file.
+ * Set the active file that mHistoryBuffer is backed up into.
+ *
+ * @param fileNumber the history file that mHistoryBuffer is backed up into.
*/
- private void createActiveFile() {
- final AtomicFile file = getFile(mFileNumbers.get(mFileNumbers.size() - 1));
+ private void setActiveFile(int fileNumber) {
+ mActiveFile = getFile(fileNumber);
if (DEBUG) {
- Slog.d(TAG, "activeHistoryFile:" + file.getBaseFile().getPath());
+ Slog.d(TAG, "activeHistoryFile:" + mActiveFile.getBaseFile().getPath());
}
- if (!file.exists()) {
- try {
- file.truncate();
- } catch (IOException e) {
- Slog.e(TAG, "Error creating history file "+ file.getBaseFile().getPath(), e);
- }
- }
- mActiveFile = file;
}
/**
@@ -189,7 +182,7 @@
* When {@link #mHistoryBuffer} reaches {@link BatteryStatsImpl.Constants#MAX_HISTORY_BUFFER},
* create next history file.
*/
- public void createNextFile() {
+ public void startNextFile() {
if (mFileNumbers.isEmpty()) {
Slog.wtf(TAG, "mFileNumbers should never be empty");
return;
@@ -198,7 +191,7 @@
// number plus one.
final int next = mFileNumbers.get(mFileNumbers.size() - 1) + 1;
mFileNumbers.add(next);
- createActiveFile();
+ setActiveFile(next);
// if free disk space is less than 100MB, delete oldest history file.
if (!hasFreeDiskSpace()) {
@@ -224,7 +217,7 @@
}
mFileNumbers.clear();
mFileNumbers.add(0);
- createActiveFile();
+ setActiveFile(0);
}
/**
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index c04a249..e52f8e1 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -3685,7 +3685,7 @@
Slog.d(TAG, "addHistoryBufferLocked writeHistoryLocked takes ms:"
+ (SystemClock.uptimeMillis() - start));
}
- mBatteryStatsHistory.createNextFile();
+ mBatteryStatsHistory.startNextFile();
mHistoryBuffer.setDataSize(0);
mHistoryBuffer.setDataPosition(0);
mHistoryBuffer.setDataCapacity(mConstants.MAX_HISTORY_BUFFER / 2);
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 69a7c4d..a67cb34 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -258,7 +258,7 @@
} else if (base::StartsWith(name, "[anon:libc_malloc]")) {
which_heap = HEAP_NATIVE;
} else if (base::StartsWith(name, "[stack")) {
- which_heap = HEAP_NATIVE;
+ which_heap = HEAP_STACK;
} else if (base::EndsWith(name, ".so")) {
which_heap = HEAP_SO;
is_swappable = true;
diff --git a/core/res/res/layout/accessibility_button_chooser.xml b/core/res/res/layout/accessibility_button_chooser.xml
index 480defb..383780a 100644
--- a/core/res/res/layout/accessibility_button_chooser.xml
+++ b/core/res/res/layout/accessibility_button_chooser.xml
@@ -40,6 +40,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="56dp"
+ android:id="@+id/accessibility_button_prompt_prologue"
android:textAppearance="?attr/textAppearanceMedium"
android:text="@string/accessibility_button_prompt_text"
android:gravity="start|center_vertical"
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 78b46ef..6c30f6b 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"የጣት አሻራ አዶ"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"በመልክ መክፈቻ ሃርድዌርን ማስተዳደር"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"መተግበሪያው ጥቅም ላይ እንዲውሉ የፊት ቅንብር ደንቦችን ለማከል እና ለመሰረዝ የሚያስችሉ ስልቶችን እንዲያስጀምር ያስችለዋል።"</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"በመልክ መክፈት ሃርድዌርን መጠቀም"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"መተግበሪያው የመልክ መክፈቻ ሃርድዌርን ለማረጋገጥ እንዲጠቀም ያስችለዋል"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"በመልክ መክፈት"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"የእርስዎን ፊት ዳግመኛ ያስመዝግቡ"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"ማንነትን ለይቶ ማወቅን ለማሻሻል፣ እባክዎ የእርስዎን ፊት ዳግም ያስመዝግቡ"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"ትክክለኛ የፊት ውሂብ ማንሳት አልተቻለም። እንደገና ይሞክሩ።"</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"መልክን ማረጋገጥ አይቻልም። ሃርድዌር የለም።"</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"በመልክ መክፈትን እንደገና ይሞክሩ።"</string>
<string name="face_error_no_space" msgid="2712120617457553825">"አዲስ የመልክ ውውሂብ ማስቀመጥ አልተቻለም። መጀመሪያ የድሮውን ይሰርዙት።"</string>
<string name="face_error_canceled" msgid="283945501061931023">"የፊት ሥርዓተ ክወና ተሰርዟል።"</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"በመልክ መክፈት በተጠቃሚ ተሰርዟል።"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"ከልክ በላይ ብዙ ሙከራዎች። በኋላ ላይ እንደገና ይሞክሩ።"</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"በጣም ብዙ ሙከራዎች። በመልክ መክፈት ተሰናክሏል።"</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"ፊትን ማረጋገጥ አይቻልም። እንደገና ይሞክሩ።"</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"በመልክ መክፈትን አላቀናበሩም።"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"በመልክ መክፈት መስጫ በዚህ መሣሪያ ላይ አይደገፍም።"</string>
<string name="face_name_template" msgid="7004562145809595384">"ፊት <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"የመሞከሪያ ጥቅል ሁነታን ለማሰናከል የፋብሪካ ዳግም ቅንብርን ይሞክሩ።"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"በዩኤስቢ ወደብ ውስጥ ፈሳሽ ወይም ፍርስራሽ"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"የዩኤስቢ ወደብ በራስ-ሰር ተሰናክሏል። የበለጠ ለመረዳት መታ ያድርጉ።"</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"የዩኤስቢ ወደቡን መጠቀም ችግር የለውም"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"ስልክ ከእንግዲህ ፈሳሽ ወይም ፍርስራሽ አላገኘም።"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"የሳንካ ሪፖርትን በመውሰድ ላይ…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"የሳንካ ሪፖርት ይጋራ?"</string>
@@ -1503,7 +1493,7 @@
<string name="find_next" msgid="5742124618942193978">"ቀጣዩን አግኝ"</string>
<string name="find_previous" msgid="2196723669388360506">"ቀዳሚውን አግኝ"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"የስፍራ ጥየቃ ቅፅ<xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"የስፍራ ጥየቃ"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"የአካባቢ ጥየቃ"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">" በ፡<xliff:g id="NAME">%1$s</xliff:g>(<xliff:g id="SERVICE">%2$s</xliff:g>) ተጠየቀ"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"አዎ"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"አይ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index dfc316c..3ea8779d 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -561,15 +561,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"رمز بصمة الإصبع"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"إدارة أجهزة \"فتح القفل بالوجه\""</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"السماح للتطبيق باستدعاء طرق لإضافة نماذج من الوجوه وحذفها"</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"استخدام أجهزة \"فتح القفل بالوجه\""</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"السماح للتطبيق باستخدام أجهزة \"فتح القفل بالوجه\" لإجراء المصادقة"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"فتح القفل بالوجه"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"إعادة تسجيل وجهك"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"لتحسين قدرة الجهاز على معرفة وجهك، يُرجى إعادة تسجيل الوجه."</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"تعذّر تسجيل بيانات دقيقة للوجه. حاول مرة أخرى."</string>
@@ -595,20 +591,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"يتعذّر التحقُّق من الوجه. الجهاز غير مُتاح."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"حاول استخدام \"فتح القفل بالوجه\" مرة أخرى."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"يتعذَّر تخزين بيانات الوجه الجديد. احذف الوجه القديم أولاً."</string>
<string name="face_error_canceled" msgid="283945501061931023">"تمّ إلغاء عملية مصادقة الوجه."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"ألغى المستخدم \"فتح القفل بالوجه\"."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"تمّ إجراء محاولات كثيرة. أعِد المحاولة لاحقًا."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"تم إجراء عدد كبير جدًا من المحاولات. وتم إيقاف \"فتح القفل بالوجه\"."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"يتعذّر التحقق من الوجه. حاول مرة أخرى."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"لم يسبق لك إعداد \"فتح القفل بالوجه\"."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"\"فتح القفل بالوجه\" غير متوفر على هذا الجهاز."</string>
<string name="face_name_template" msgid="7004562145809595384">"الوجه <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -709,7 +700,7 @@
<item msgid="1103601433382158155">"فاكس العمل"</item>
<item msgid="1735177144948329370">"فاكس المنزل"</item>
<item msgid="603878674477207394">"جهاز نداء"</item>
- <item msgid="1650824275177931637">"آخر"</item>
+ <item msgid="1650824275177931637">"غير ذلك"</item>
<item msgid="9192514806975898961">"مخصص"</item>
</string-array>
<string-array name="emailAddressTypes">
@@ -1210,7 +1201,7 @@
<string name="no" msgid="5141531044935541497">"إلغاء"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"تنبيه"</string>
<string name="loading" msgid="7933681260296021180">"جارٍ التحميل…"</string>
- <string name="capital_on" msgid="1544682755514494298">"تشغيل"</string>
+ <string name="capital_on" msgid="1544682755514494298">"مفعّلة"</string>
<string name="capital_off" msgid="6815870386972805832">"إيقاف"</string>
<string name="whichApplication" msgid="4533185947064773386">"إكمال الإجراء باستخدام"</string>
<string name="whichApplicationNamed" msgid="8260158865936942783">"إكمال الإجراء باستخدام %1$s"</string>
@@ -1454,8 +1445,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"يمكنك إجراء إعادة ضبط على إعدادات المصنع لإيقاف وضع \"مفعِّل اختبار\"."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"السوائل والشوائب في منفذ USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"تمّ إيقاف منفذ USB تلقائيًا. انقُر لمعرفة المزيد من المعلومات."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"مسموح باستخدام منفذ USB"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"لم يَعُد الهاتف يكتشف سوائل أو شوائب."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"جارٍ الحصول على تقرير الخطأ…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"هل تريد مشاركة تقرير الخطأ؟"</string>
@@ -1595,7 +1585,7 @@
<string name="find_next" msgid="5742124618942193978">"بحث عن التطابق التالي"</string>
<string name="find_previous" msgid="2196723669388360506">"بحث عن التطابق السابق"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"طلب الموقع من <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"طلب الموقع"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"طلب الموقع الجغرافي"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"مطلوب من <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"نعم"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"لا"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index f971703..3213774 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ফিংগাৰপ্ৰিণ্ট আইকন"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"মুখাৱয়বৰদ্বাৰা আনলক হার্ডৱেৰ পৰিচালনা কৰক"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"মুখমণ্ডলৰ টেম্প্লেট যোগ কৰাৰ বা মচাৰ পদ্ধতি কামত লগাবলৈ আহ্বান কৰিবলৈ এপটোক অনুমতি দিয়ে।"</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"মুখাৱয়বৰদ্বাৰা আনলক হার্ডৱেৰ ব্যৱহাৰ কৰক"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"বিশ্বাসযোগ্যতা প্ৰমাণীকৰণৰ বাবে এপ্ক মুখাৱয়বৰদ্বাৰা আনলক কৰা হাৰ্ডৱেৰ ব্যৱহাৰ কৰিবলৈ দিয়ে"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"মুখাৱয়বৰদ্বাৰা আনলক কৰা সুবিধা"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"আপোনাৰ মুখমণ্ডল পুনৰ পঞ্জীয়ণ কৰক"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"চিনাক্তকৰণৰ সুবিধাটো উন্নত কৰিবলৈ, অনুগ্ৰহ কৰি আপোনাৰ মুখমণ্ডল পুনৰ পঞ্জীয়ন কৰক"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"সঠিক মুখমণ্ডলৰ ডেটা কেপচাৰ নহ’ল। আকৌ চেষ্টা কৰক।"</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"মুখমণ্ডল সত্যাপন কৰিব পৰা নগ’ল। হাৰ্ডৱেৰ নাই।"</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"পুনৰ মুখাৱয়বৰদ্বাৰা আনলক কৰি চাওক।"</string>
<string name="face_error_no_space" msgid="2712120617457553825">"নতুন মুখমণ্ডলৰ ডেটা জমা কৰিব পৰা নাই। প্ৰথমে পুৰণি এখন মচক।"</string>
<string name="face_error_canceled" msgid="283945501061931023">"মুখমণ্ডলৰ প্ৰক্ৰিয়া বাতিল কৰা হ’ল।"</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"ব্যৱহাৰকাৰীয়ে মুখাৱয়বৰদ্বাৰা আনলক কৰাটো বাতিল কৰিছে।"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"অত্যধিক ভুল প্ৰয়াস। কিছুসময়ৰ পাছত আকৌ চেষ্টা কৰক।"</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"অতি বেছি প্ৰয়াস। মুখাৱয়বৰদ্বাৰা আনলক কৰাটো অক্ষম কৰা হৈছে।"</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"মুখমণ্ডল সত্যাপন কৰিব পৰা নগ’ল। আকৌ চেষ্টা কৰক।"</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"আপুনি মুখাৱয়বৰদ্বাৰা আনলক কৰাটো ছেট আপ কৰা নাই।"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"এই ডিভাইচটোত মুখাৱয়বৰদ্বাৰা আনলক কৰা সুবিধাটো নচলে।"</string>
<string name="face_name_template" msgid="7004562145809595384">"মুখমণ্ডল <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"টেষ্ট হাৰনেছ ম’ড অক্ষম কৰিবলৈ ফেক্টৰী ৰিছেট কৰক।"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"ইউএছবি প’ৰ্টত তৰল বা ধূলি-মাকতি আছে"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"ইউএছবি প’ৰ্ট স্বয়ংক্ৰিয়ভাৱে অক্ষম কৰা হয়। অধিক জানিবৰ বাবে টিপক।"</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"ইউএছবি প’ৰ্ট ব্যৱহাৰ কৰাত সমস্যা নাই"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"ফ’নটোৱে তৰল বা ধূলি-মাকতি আৰু চিনাক্ত নকৰে।"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"বাগ সম্পর্কীয় অভিযোগ গ্ৰহণ কৰি থকা হৈছে…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"বাগ সম্পর্কীয় অভিযোগ শ্বেয়াৰ কৰিবনে?"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 6eac6fe..815cf72 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Barmaq izi ikonası"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"üz kilidi avadanlığını idarə edin"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Proqramdan istifadə üçün barmaq izi şablonlarını əlavə etmək və silmək məqsədilə üsullara müraciət etməyə imkan verir."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"üz kilidi avadanlığından istifadə edin"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"İdentifikasiya üçün tətbiqin üz kilidi avadanlığından istifadə etməsinə icazə verir"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Üz kilidi"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Üzünüzü yenidən qeydiyyatdan keçirin"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Tanınmanı təkmilləşdirmək üçün üzünüzü yenidən qeydiyyatdan keçirin"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Dəqiq üz datası əldə edilmədi. Yenidən cəhd edin."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Üz doğrulanmadı. Avadanlıq əlçatan deyil."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Üz kilidini yenidən sınayın."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Yeni üz datası saxlanmadı. Əvvəlcə köhnə olanı silin."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Üz əməliyyatı ləğv edildi."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"İstifadəçi üz kilidini ləğv edib."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Həddindən çox cəhd. Sonraya saxlayın."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Həddindən çox cəhd. Üz kilidi deaktiv edildi."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Üz doğrulanmadı. Yenidən cəhd edin."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Üz kilidi quraşdırmamısınız."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Üz kilidi bu cihazda dəstəklənmir."</string>
<string name="face_name_template" msgid="7004562145809595384">"Üz <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Test Rejimini deaktiv etmək üçün fabrika ayarlarına sıfırlayın."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB portuna maye sızıb və ya qırılıb"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB portu avtomatik deaktiv edildi. Ətraflı məlumat üçün klikləyin."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB portundan istifadə etmək üçün OK"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Telefon artıq maye və ya nasazlığı aşkarlamayacaq."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Baq hesabatı verilir..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Baq hesabatı paylaşılsın?"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 8f138d4..8f1bb2c 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -552,15 +552,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona otiska prsta"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"upravljanje hardv. za otključavanje licem"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Dozvoljava da aplikacija aktivira metode za dodavanje i brisanje šablona lica radi korišćenja."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"korišćenje hardvera za otključavanje licem"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Dozvoljava da aplikacija koristi hardver za otključavanje licem radi potvrde identiteta"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Otključavanje licem"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Ponovo registrujte lice"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Da biste poboljšali prepoznavanje, ponovo registrujte lice"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Snimanje lica nije uspelo. Probajte ponovo."</string>
@@ -586,20 +582,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Provera lica nije uspela. Hardver nije dostupan."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Probajte ponovo otključavanje licem."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Novi podaci o licu nisu sačuvani. Prvo izbrišete prethodne."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Obrada lica je otkazana."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Korisnik je otkazao otključavanje licem"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Previše pokušaja. Probajte ponovo kasnije."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Previše pokušaja. Otključavanje licem je onemogućeno."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Provera lica nije uspela. Probajte ponovo."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Niste podesili otključavanje licem"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Otključavanje licem nije podržano na ovom uređaju"</string>
<string name="face_name_template" msgid="7004562145809595384">"Lice <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1388,8 +1379,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Obavite resetovanje na fabrička podešavanja da biste onemogućili režim probnog korišćenja."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Tečnost ili nečistoća u USB portu"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB port je automatski isključen. Dodirnite da biste saznali više."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Korišćenje USB porta je dozvoljeno"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Telefon više ne otkriva tečnost ili nečistoću."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Izveštaj o grešci se generiše…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Želite li da podelite izveštaj o grešci?"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index e3a5502..6d8bafa 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -555,15 +555,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Значок адбіткаў пальцаў"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"кіраваць апаратным забеспячэннем для распазнавання твару"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Праграма зможа дадаваць і выдаляць шаблоны твару."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"выкарыстоўваць апаратнае забеспячэнне для распазнавання твару"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Для аўтэнтыфікацыі праграма зможа ўжываць апаратнае забеспячэнне для распазнавання твару"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Распазнаванне твару"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Паўтарыце рэгістрацыю твару"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Каб палепшыць распазнавальнасць, яшчэ раз выканайце рэгістрацыю твару"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Не атрымалася распазнаць твар. Паўтарыце спробу."</string>
@@ -589,20 +585,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Твар не спраўджаны. Абсталяванне недаступнае."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Выканайце распазнаванне твару паўторна."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Новыя даныя пра твар не захаваны. Спачатку выдаліце старыя."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Распазнаванне твару скасавана."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Распазнаванне твару скасавана карыстальнікам."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Занадта шмат спроб. Паўтарыце спробу пазней."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Занадта шмат спроб. Распазнаванне твару выключана."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Не ўдалося спраўдзіць твар. Паўтарыце спробу."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Вы не наладзілі распазнаванне твару."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"На гэтай прыладзе распазнаванне твару не падтрымліваецца."</string>
<string name="face_name_template" msgid="7004562145809595384">"Твар <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1410,8 +1401,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Каб выключыць тэставы рэжым, скіньце налады да заводскіх значэнняў."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Вадкасць або смецце ў порце USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Порт USB аўтаматычна адключаны. Каб даведацца больш, націсніце тут."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Порт USB можна выкарыстоўваць"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Тэлефон выявіў, што вадкасці і смецця больш няма."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Стварэнне справаздачы пра памылку…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Падзяліцца справаздачай пра памылку?"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 0e85554..9b36914 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Икона за отпечатък"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"управление на хардуера за отключване с лице"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Разрешава на прил. да извиква методи за добавяне и изтриване на лицеви шаблони за ползване"</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"използване на хардуера за отключване с лице"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Разрешава на приложението да използва хардуера за отключване с лице с цел удостоверяване"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Отключване с лице"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Регистрирайте отново лицето си"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"С цел подобряване на разпознаването регистрирайте отново лицето си"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Лицето не бе заснето точно. Опитайте отново."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Лицето не може да се потвърди. Хардуерът не е налице."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Опитайте отново да отключите с лице."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Не може да се запази ново лице. Първо изтрийте старо."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Операцията с лице е анулирана."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Отключването с лице е анулирано от потребителя."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Твърде много опити. Опитайте отново по-късно."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Твърде много опити. Отключването с лице е деактивирано."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Лицето не може да се потвърди. Опитайте отново."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Не сте настроили отключването с лице."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Отключването с лице не се поддържа на това устройство."</string>
<string name="face_name_template" msgid="7004562145809595384">"Лице <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Възстановете фабричните настройки, за да деактивирате режима за тестова среда."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Течност или замърсяване в USB порта"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB портът е деактивиран автоматично. Докоснете, за да научите повече."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Можете да използвате USB порта"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Телефонът вече не открива течности или замърсяване."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Сигналът за програмна грешка се извлича…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Да се сподели ли сигналът за програмна грешка?"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 8f1cf57..19b0103 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"আঙ্গুলের ছাপ আইকন"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"মুখের সাহায্যে আনলক করার হার্ডওয়্যার ম্যানেজ করা"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"ব্যবহার করার জন্য ফেস টেম্পলেট যোগ করা এবং মোছার পদ্ধতি গ্রহণ করতে অ্যাপটিকে অনুমতি দেয়৷"</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"মুখের সাহায্যে আনলক করার হার্ডওয়্যার ব্যবহার করা"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"অ্যাপকে যাচাইকরণের জন্য মুখের সাহায্যে আনলক করার হার্ডওয়্যার ব্যবহার করতে দেয়"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"মুখের সাহায্যে আনলক"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"আপনার ফেস আবার এনরোল করুন"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"শনাক্তকরণের উন্নতি করতে আপনার ফেস আবার এনরোল করুন"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"মুখের সঠিক ডেটা পাওয়া যায়নি। আবার চেষ্টা করুন।"</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"ফেস যাচাই করা যায়নি। হার্ডওয়্যার উপলভ্য নেই।"</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"আবার মুখের সাহায্যে আনলক করার চেষ্টা করুন।"</string>
<string name="face_error_no_space" msgid="2712120617457553825">"নতুন ফেস ডেটা স্টোর করা যায়নি। প্রথমে পুরনোটি মুছে ফেলুন।"</string>
<string name="face_error_canceled" msgid="283945501061931023">"ফেস অপারেশন বাতিল করা হয়েছে৷"</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"ব্যবহারকারী মুখের সাহায্যে আনলক বাতিল করে দিয়েছেন।"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"অনেকবার চেষ্টা করা হয়েছে। পরে আবার চেষ্টা করুন।"</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"অনেকবার চেষ্টা করেছেন। মুখের সাহায্যে আনলক করার সুবিধা বন্ধ করা হয়েছে।"</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"আপনার মুখ যাচাই করা যাচ্ছে না। আবার চেষ্টা করুন।"</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"এখনও মুখের সাহায্যে আনলক করার সুবিধা সেট-আপ করেননি।"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"এই ডিভাইসে মুখের সাহায্যে আনলক করার সুবিধাটি কাজ করে না।"</string>
<string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g> ফেস"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1367,8 +1358,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"টেস্ট হারনেস মোড বন্ধ করতে ফ্যাক্টরি রিসেট করুন।"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"ইউএসবি পোর্টে তরল পদার্থ অথবা ধুলো কণা"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"ইউএসবি পোর্ট নিজে থেকে বন্ধ করা হবে। আরও জানতে ট্যাপ করুন।"</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"ইউএসবি পোর্ট ব্যবহার করা যেতে পারে"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"ফোন আর তরল পদার্থ এবং ধুলো কণা শনাক্ত করবে না।"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"ত্রুটির প্রতিবেদন নেওয়া হচ্ছে..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"ত্রুটির প্রতিবেদন শেয়ার করবেন?"</string>
@@ -1504,7 +1494,7 @@
<string name="find_next" msgid="5742124618942193978">"পরবর্তীটি খুঁজুন"</string>
<string name="find_previous" msgid="2196723669388360506">"পূর্ববর্তীটি খুঁজুন"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"<xliff:g id="NAME">%s</xliff:g> এর থেকে অবস্থানের অনুরোধ"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"অবস্থানের অনুরোধ"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"লোকেশন জানার অনুরোধ"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"<xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>) এর দ্বারা অনুরোধকৃত"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"হ্যাঁ"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"না"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 5ef243c..cd04c6f 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -552,15 +552,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona za otisak prsta"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"upravljanje hardverom za otključavanje licem"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Omogućava aplikaciji korištenje metoda za dodavanje i brisanje šablona lica za upotrebu."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"korištenje hardvera za otključavanje licem"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Omogućava aplikaciji da za autentifikaciju koristi hardver za otključavanje licem"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Otključavanje licem"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Ponovo registrirajte lice"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Ponovo registrirajte lice da poboljšate prepoznavanje"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Lice nije snimljeno precizno. Pokušajte ponovo."</string>
@@ -586,20 +582,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Nije moguće potvrditi lice. Hardver nije dostupan."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Pokušajte ponovo s otključavanjem licem."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Nije moguće sačuvati nove podatke o licu. Prvo izbrišite stare."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Prepoznavanje lica je otkazano."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Korisnik je otkazao otključavanje licem."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Previše pokušaja. Pokušajte ponovo kasnije."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Previše pokušaja. Otključavanje licem je onemogućeno."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Nije moguće potvrditi lice. Pokušajte ponovo."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Niste postavili otključavanje licem."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Otključavanje licem nije podržano na ovom uređaju."</string>
<string name="face_name_template" msgid="7004562145809595384">"Lice <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1390,8 +1381,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Izvršite vraćanje na fabričke postavke da onemogućite način rada okvira za testiranje."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Tečnost ili nečistoće u USB priključku"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB priključak je automatski onemogućen. Dodirnite da saznate više."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB priključak je sada sigurno koristiti"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Telefon više ne detektira tečnost ili nečistoće."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Prijem izvještaja o grešci..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Podijeliti izvještaj o grešci?"</string>
@@ -1528,7 +1518,7 @@
<string name="find_next" msgid="5742124618942193978">"Nađi sljedeći"</string>
<string name="find_previous" msgid="2196723669388360506">"Nađi prethodni"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"Korisnik <xliff:g id="NAME">%s</xliff:g> je poslao zahtjev za utvrđivanje lokacije"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"Zahtjev za utvrđivanje lokacije"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"Zahtjev za lokaciju"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"Zahtjev uputio <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"Da"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"Ne"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 2050de3..8f3a1c7 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icona d\'empremta digital"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"gestiona el maquinari de desbloqueig facial"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Permet que l\'aplicació afegeixi i suprimeixi plantilles de cares que es puguin fer servir."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"utilitza el maquinari de desbloqueig facial"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Permet que l\'aplicació faci servir el maquinari de desbloqueig facial per a l\'autenticació"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Desbloqueig facial"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Torna a registrar la cara"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Per millorar el reconeixement, torna a registrar la cara"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"No es reconeix la teva cara. Torna-ho a provar."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"No es pot verificar la cara. Maquinari no disponible."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Torna a provar el desbloqueig facial."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"No es poden desar dades facials noves. Suprimeix-ne d\'antigues."</string>
<string name="face_error_canceled" msgid="283945501061931023">"S\'ha cancel·lat el reconeixement facial."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"L\'usuari ha cancel·lat el desbloqueig facial."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Massa intents. Torna-ho a provar més tard."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Massa intents. S\'ha desactivat el desbloqueig facial."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"No es pot verificar la cara. Torna-ho a provar."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"No has configurat el desbloqueig facial"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"El desbloqueig facial no és compatible amb el dispositiu."</string>
<string name="face_name_template" msgid="7004562145809595384">"Cara <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Si vols desactivar el mode Agent de prova, restableix les dades de fàbrica."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Hi ha líquid o pols al port USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"El port USB es desactiva automàticament. Toca per obtenir més informació."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Ja pots utilitzar el port USB"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"El telèfon ja no detecta líquids ni pols."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"S\'està creant l\'informe d\'errors…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Vols compartir l\'informe d\'errors?"</string>
@@ -1792,7 +1782,7 @@
<string name="restr_pin_try_later" msgid="973144472490532377">"Torna-ho a provar més tard"</string>
<string name="immersive_cling_title" msgid="8394201622932303336">"Mode de pantalla completa"</string>
<string name="immersive_cling_description" msgid="3482371193207536040">"Per sortir, llisca cap avall des de la part superior."</string>
- <string name="immersive_cling_positive" msgid="5016839404568297683">"D\'acord"</string>
+ <string name="immersive_cling_positive" msgid="5016839404568297683">"Entesos"</string>
<string name="done_label" msgid="2093726099505892398">"Fet"</string>
<string name="hour_picker_description" msgid="6698199186859736512">"Control circular de les hores"</string>
<string name="minute_picker_description" msgid="8606010966873791190">"Control circular dels minuts"</string>
@@ -2014,7 +2004,7 @@
<string name="battery_saver_off_notification_summary" msgid="1374222493681267143">"L\'estalvi de bateria s\'ha desactivat. Les funcions ja no estan restringides."</string>
<string name="battery_saver_off_alternative_notification_summary" msgid="4340727818546508436">"L\'estalvi de bateria s\'ha desactivat. Les funcions ja no estan restringides."</string>
<string name="mime_type_folder" msgid="7111951698626315204">"Carpeta"</string>
- <string name="mime_type_apk" msgid="5518003630972506900">"Aplicació per a Android"</string>
+ <string name="mime_type_apk" msgid="5518003630972506900">"Aplicació d\'Android"</string>
<string name="mime_type_generic" msgid="6833871596845900027">"Fitxer"</string>
<string name="mime_type_generic_ext" msgid="8450275970061657174">"Fitxer <xliff:g id="EXTENSION">%1$s</xliff:g>"</string>
<string name="mime_type_audio" msgid="6289777657172050926">"Àudio"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 6feb718f..ecb0d6c 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -555,15 +555,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona otisku prstů"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"správa hardwaru k odemknutí obličejem"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Umožňuje aplikaci volat metody k přidání a smazání šablon obličeje, které budou použity."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"použití hardwaru k odemknutí obličejem"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Umožňuje aplikaci provést ověření pomocí hardwaru k odemknutí obličejem"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Odemknutí obličejem"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Zaznamenejte obličej znovu"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Chcete-li rozpoznání zdokonalit, zaznamenejte obličej znovu"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Obličej se nepodařilo zachytit. Zkuste to znovu."</string>
@@ -589,20 +585,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Obličej nelze ověřit. Hardware není dostupný."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Zopakujte odemknutí obličejem."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Údaje o novém obličeji nelze uložit. Nejdřív vymažte starý."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Operace snímání obličeje byla zrušena."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Odemknutí obličejem zrušil uživatel."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Příliš mnoho pokusů. Zkuste to později."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Příliš mnoho pokusů. Odemknutí obličejem bylo deaktivováno."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Obličej se nepodařilo ověřit. Zkuste to znovu."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Ověření obličejem nemáte nastavené."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Odemknutí obličejem na tomto zařízení není podporováno."</string>
<string name="face_name_template" msgid="7004562145809595384">"Obličej <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1410,8 +1401,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Chcete-li deaktivovat režim správce testů, restartujte zařízení do továrního nastavení."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Kapalina nebo nečistota v portu USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Port USB byl automaticky deaktivován. Klepnutím zobrazíte další informace."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Port USB lze bezpečně použít"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Telefon již nedetekuje kapaliny ani nečistoty."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Vytváření zprávy o chybě…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Sdílet zprávu o chybě?"</string>
@@ -1549,7 +1539,7 @@
<string name="find_next" msgid="5742124618942193978">"Najít další"</string>
<string name="find_previous" msgid="2196723669388360506">"Najít předchozí"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"Požadavek na informace o poloze od uživatele <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"Požadavek na informace o poloze"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"Žádost o informaci o poloze"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"Požadavek od uživatele <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"Ano"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"Ne"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index ffa382b..32ee771 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikon for fingeraftryk"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"administrere hardware til ansigtslås"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Tillader, at appen kan bruge metoder til at tilføje og slette ansigtsskabeloner."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"bruge hardware til ansigtslås"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Tillader, at appen bruger hardware til ansigtslås til godkendelse"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Ansigtslås"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Registrer dit ansigt igen"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Registrer dit ansigt igen for at forbedre genkendelsen af det"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Der blev ikke registreret ansigtsdata. Prøv igen."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Ansigt ikke bekræftet. Hardware ikke tilgængelig."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Prøv ansigtslås igen."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Der kan ikke gemmes flere nye ansigter. Slet et gammelt."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Ansigtshandlingen blev annulleret."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Ansigtslås blev annulleret af brugeren."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Du har prøvet for mange gange. Prøv igen senere."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Du har brugt for mange forsøg. Ansigtslås er deaktiveret."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Ansigtet kan ikke genkendes. Prøv igen."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Du har ikke konfigureret ansigtslås."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Ansigtslås understøttes ikke på denne enhed."</string>
<string name="face_name_template" msgid="7004562145809595384">"Ansigt <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Gendan fabriksindstillingerne for at deaktivere tilstanden Testsele."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Væske eller snavs i USB-porten"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-porten deaktiveres automatisk. Tryk for at få flere oplysninger."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB-porten kan bruges"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Telefonen registrerer ikke længere væske og snavs."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Opretter fejlrapport…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Vil du dele fejlrapporten?"</string>
@@ -1989,7 +1979,7 @@
<string name="harmful_app_warning_title" msgid="8982527462829423432">"Der er registreret en skadelig app"</string>
<string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> anmoder om tilladelse til at vise eksempler fra <xliff:g id="APP_2">%2$s</xliff:g>"</string>
<string name="screenshot_edit" msgid="7867478911006447565">"Rediger"</string>
- <string name="volume_dialog_ringer_guidance_vibrate" msgid="8902050240801159042">"Telefonen vil vibrere ved opkald og notifikationer"</string>
+ <string name="volume_dialog_ringer_guidance_vibrate" msgid="8902050240801159042">"Telefonen vibrerer ved opkald og notifikationer"</string>
<string name="volume_dialog_ringer_guidance_silent" msgid="2128975224280276122">"Der afspilles ikke lyd ved opkald og notifikationer"</string>
<string name="notification_channel_system_changes" msgid="5072715579030948646">"Systemændringer"</string>
<string name="notification_channel_do_not_disturb" msgid="6766940333105743037">"Forstyr ikke"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 5e0ccd0..6d94a77 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingerabdruck-Symbol"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"Face Unlock-Hardware verwalten"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Ermöglicht der App, Gesichtsvorlagen hinzuzufügen oder zu entfernen."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"Face Unlock-Hardware verwenden"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Ermöglicht der App, zu Authentifizierungszwecken Face Unlock-Hardware zu verwenden"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Face Unlock"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Gesicht neu scannen lassen"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Für bessere Erkennung Gesicht neu scannen lassen"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Gesichtsdaten nicht gut erfasst. Erneut versuchen."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Gesicht nicht erkannt. Hardware nicht verfügbar."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Face Unlock noch einmal versuchen."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Kein Speicherplatz frei. Bitte erst ein Gesicht löschen."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Gesichtserkennung abgebrochen."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Face Unlock vom Nutzer abgebrochen."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Zu viele Versuche, bitte später noch einmal versuchen"</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Zu viele Versuche. Face Unlock wurde deaktiviert."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Gesichtsprüfung nicht möglich. Noch mal versuchen."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Face Unlock ist nicht eingerichtet."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Face Unlock wird auf diesem Gerät nicht unterstützt."</string>
<string name="face_name_template" msgid="7004562145809595384">"Gesicht <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Setz das Gerät auf die Werkseinstellungen zurück, um den Test-Harnischmodus zu deaktivieren."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Flüssigkeiten oder Fremdkörper im USB-Port"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Der USB-Port wird automatisch deaktiviert. Für weitere Informationen tippen."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB-Port kann wieder verwendet werden"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Das Smartphone erkennt keine Flüssigkeiten oder Fremdkörper mehr."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Fehlerbericht wird abgerufen…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Fehlerbericht teilen?"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 07cae0a..96e3580 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ícono de huella digital"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"administrar el hardware de Desbloqueo facial"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Permite que la app emplee métodos para agregar y borrar plantillas de rostros para su uso."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"usar el hardware de Desbloqueo facial"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Permite que la app use el hardware de Desbloqueo facial con fines de autenticación"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Desbloqueo facial"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Vuelve a registrar tu rostro"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Para mejorar el reconocimiento, vuelve a registrar tu rostro"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Datos faciales imprecisos. Vuelve a intentarlo."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"No se verificó el rostro. Hardware no disponible."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Vuelve a probar el Desbloqueo facial."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"No hay espacio para datos faciales nuevos. Borra uno viejo."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Se canceló el reconocimiento facial."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"El usuario canceló el Desbloqueo facial."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Demasiados intentos. Inténtalo de nuevo más tarde."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Demasiados intentos. Se inhabilitó el Desbloqueo facial."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"No se pudo verificar la cara. Vuelve a intentarlo."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"No configuraste el Desbloqueo facial."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"No se admite el Desbloqueo facial en este dispositivo."</string>
<string name="face_name_template" msgid="7004562145809595384">"Rostro <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Restablece la configuración de fábrica para inhabilitar el modo de agente de prueba."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Hay líquido o suciedad en el puerto USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"El puerto USB se inhabilitó automáticamente. Presiona para obtener más información."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Se puede usar el puerto USB"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"El teléfono ya no detecta líquidos o suciedad."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Realizando un informe de errores…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"¿Compartir informe de errores?"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 7261eb0..0e4108c 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icono de huella digital"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"gestionar el hardware de desbloqueo facial"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Permite que la app use métodos para añadir y suprimir plantillas de caras para su uso."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"Utilizar hardware de desbloqueo facial"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Permite que la aplicación utilice el hardware de desbloqueo facial para autenticarte"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Desbloqueo facial"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Volver a registrar la cara"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Para mejorar el reconocimiento, vuelve a registrar tu cara"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Datos faciales no reconocidos. Vuelve a intentarlo."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"No se puede verificar. Hardware no disponible."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Vuelve a probar el desbloqueo facial."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Para guardar nuevos datos faciales, borra otros antiguos."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Se ha cancelado el reconocimiento facial."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"El usuario ha cancelado el desbloqueo facial."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Demasiados intentos. Inténtalo de nuevo más tarde."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Muchos intentos. Se ha inhabilitado el desbloqueo facial."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"No se ha verificado tu cara. Vuelve a intentarlo."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"No has configurado el desbloqueo facial."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"El desbloqueo facial no está disponible en este dispositivo."</string>
<string name="face_name_template" msgid="7004562145809595384">"Cara <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Restablece los ajustes de fábrica para inhabilitar el modo de agente de prueba."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Se ha detectado líquido o suciedad en el puerto USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"El puerto USB se ha inhabilitado automáticamente. Toca para obtener más información."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Se puede utilizar el puerto USB"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"El teléfono ya no detecta líquidos ni suciedad."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Creando informe de errores…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"¿Compartir informe de errores?"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 42ce4ef..b447922 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Sõrmejälje ikoon"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"hallata Face Unlocki riistvara"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Lubab rakendusel tühistada meetodid kasutatavate näomallide lisamiseks ja kustutamiseks."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"kasutada Face Unlocki riistvara"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Võimaldab rakendusel autentimiseks kasutada Face Unlocki riistvara"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Face Unlock"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Registreerige oma nägu uuesti"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Tuvastamise parandamiseks registreerige oma nägu uuesti"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Näoandmeid ei saanud jäädvustada. Proovige uuesti."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Nägu ei saa kinnitada. Riistvara pole saadaval."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Proovige Face Unlocki uuesti."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Uue näo andmeid ei saa salvestada. Kustutage enne vanad."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Näotuvastuse toiming tühistati."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Kasutaja tühistas Face Unlocki."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Liiga palju katseid. Proovige hiljem uuesti."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Liiga palju katseid. Face Unlock on keelatud."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Nägu ei saa kinnitada. Proovige uuesti."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Face Unlocki ei ole seadistatud."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Seade ei toeta Face Unlocki."</string>
<string name="face_name_template" msgid="7004562145809595384">"Nägu <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Testrakendirežiimi keelamiseks taastage tehaseseaded."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB-pordis on vedelik või mustus"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-port on automaatselt keelatud. Puudutage lisateabe saamiseks."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB-porti tohib kasutada"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Telefon ei tuvasta enam vedelikku ega mustust."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Veaaruande võtmine …"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Kas jagada veaaruannet?"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 00e9de9..0e8e66b 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Hatz-markaren ikonoa"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"kudeatu aurpegiaren bidez desblokeatzeko hardwarea"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Aurpegi-txantiloiak gehitu eta ezabatzeko metodoei dei egitea baimentzen dio aplikazioari."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"erabili aurpegiaren bidez desblokeatzeko hardwarea"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Autentifikazioa egiteko aurpegiaren bidez desblokeatzeko hardwarea erabiltzeko baimena ematen dio aplikazioari"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Aurpegiaren bidez desblokeatzeko aukera"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Erregistratu aurpegia berriro"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Ezagutzea hobetzeko, erregistratu aurpegia berriro"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Ezin izan dira bildu argazkiaren datu zehatzak. Saiatu berriro."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Ezin da egiaztatu aurpegia. Hardwarea ez dago erabilgarri."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Saiatu berriro aurpegiaren bidez desblokeatzen."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Ezin dira gorde aurpegiaren datu berriak. Ezabatu zaharrak."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Utzi da aurpegiaren bidezko eragiketa."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Erabiltzaileak bertan behera utzi du aurpegiaren bidez desblokeatzea."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Saiakera gehiegi egin dituzu. Saiatu berriro geroago."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Saiakera gehiegi egin dira. Aurpegiaren bidez desblokeatzeko aukera desgaitu egin da."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Ezin da egiaztatu aurpegia. Saiatu berriro."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Ez duzu konfiguratu aurpegiaren bidez desblokeatzeko aukera."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Gailu honek ez du onartzen aurpegiaren bidez desblokeatzea."</string>
<string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g> aurpegia"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -621,8 +612,8 @@
<string name="permdesc_connection_manager" msgid="5925480810356483565">"Telekomunikabideekiko konexioak kudeatzea baimentzen die aplikazioei."</string>
<string name="permlab_bind_incall_service" msgid="6773648341975287125">"erabili pantaila deiak abian direnean"</string>
<string name="permdesc_bind_incall_service" msgid="8343471381323215005">"Erabiltzaileak deiaren pantaila noiz eta nola ikusten duen kontrolatzeko aukera ematen die aplikazioei."</string>
- <string name="permlab_bind_connection_service" msgid="3557341439297014940">"elkarreragin telefono-zerbitzuekin"</string>
- <string name="permdesc_bind_connection_service" msgid="4008754499822478114">"Deiak egiteko eta jasotzeko telefonia-zerbitzuekin elkarreragitea baimentzen die aplikazioei."</string>
+ <string name="permlab_bind_connection_service" msgid="3557341439297014940">"jardun interakzioan telefono-zerbitzuekin"</string>
+ <string name="permdesc_bind_connection_service" msgid="4008754499822478114">"Deiak egiteko eta jasotzeko telefonia-zerbitzuekin interakzioan aritzea baimentzen die aplikazioei."</string>
<string name="permlab_control_incall_experience" msgid="9061024437607777619">"eskaini erabiltzaileentzako aukerak deiak abian direnean"</string>
<string name="permdesc_control_incall_experience" msgid="915159066039828124">"Deiak abian direnean erabiltzeko aukera eskaintzea baimentzen die aplikazioei."</string>
<string name="permlab_readNetworkUsageHistory" msgid="7862593283611493232">"irakurri sare-erabileraren historia"</string>
@@ -978,8 +969,8 @@
<string name="searchview_description_submit" msgid="2688450133297983542">"Bidali kontsulta"</string>
<string name="searchview_description_voice" msgid="2453203695674994440">"Ahozko bilaketa"</string>
<string name="enable_explore_by_touch_warning_title" msgid="7460694070309730149">"\"Arakatu ukituta\" eginbidea gaitu nahi duzu?"</string>
- <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> zerbitzuak \"Arakatu ukituta\" eginbidea gaitu nahi du. Eginbide hori aktibatuta dagoenean, hatzaren azpian duzunaren azalpena ikus edo entzun dezakezu, edo tabletarekin elkarrekintzan aritzeko keinuak egin ditzakezu."</string>
- <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> zerbitzuak \"Arakatu ukituta\" eginbidea gaitu nahi du. Eginbide hori aktibatuta dagoenean, hatzaren azpian duzunaren azalpena ikus edo entzun dezakezu, edo telefonoarekin elkarrekintzan aritzeko keinuak egin ditzakezu."</string>
+ <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="8655887539089910577">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> zerbitzuak \"Arakatu ukituta\" eginbidea gaitu nahi du. Eginbide hori aktibatuta dagoenean, hatzaren azpian duzunaren azalpena ikus edo entzun dezakezu, edo tabletarekin interakzioan aritzeko keinuak egin ditzakezu."</string>
+ <string name="enable_explore_by_touch_warning_message" product="default" msgid="2708199672852373195">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> zerbitzuak \"Arakatu ukituta\" eginbidea gaitu nahi du. Eginbide hori aktibatuta dagoenean, hatzaren azpian duzunaren azalpena ikus edo entzun dezakezu, edo telefonoarekin interakzioan aritzeko keinuak egin ditzakezu."</string>
<string name="oneMonthDurationPast" msgid="7396384508953779925">"Duela hilabete"</string>
<string name="beforeOneMonthDurationPast" msgid="909134546836499826">"Duela hilabete baino gutxiago"</string>
<plurals name="last_num_days" formatted="false" msgid="5104533550723932025">
@@ -1160,7 +1151,7 @@
<string name="use_a_different_app" msgid="8134926230585710243">"Erabili beste aplikazio bat"</string>
<string name="clearDefaultHintMsg" msgid="3252584689512077257">"Garbitu aplikazio lehenetsia Sistemaren ezarpenak > Aplikazioak > Deskargatutakoak atalean."</string>
<string name="chooseActivity" msgid="7486876147751803333">"Aukeratu ekintza bat"</string>
- <string name="chooseUsbActivity" msgid="6894748416073583509">"Aukeratu USB gailurako aplikazioa"</string>
+ <string name="chooseUsbActivity" msgid="6894748416073583509">"Aukeratu USB bidezko gailurako aplikazioa"</string>
<string name="noApplications" msgid="2991814273936504689">"Ez dago ekintza hori egin dezakeen aplikaziorik."</string>
<string name="aerr_application" msgid="250320989337856518">"Gelditu egin da <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
<string name="aerr_process" msgid="6201597323218674729">"Gelditu egin da <xliff:g id="PROCESS">%1$s</xliff:g>"</string>
@@ -1367,8 +1358,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Proba-materialaren modua desgaitzeko, berrezarri jatorrizko datuak."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Likidoa edo zikinkeriak daude USB atakan"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB ataka automatikoki desgaitu da. Informazio gehiago lortzeko, sakatu hau."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Erabiltzeko moduan dago USB ataka"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Telefonoak ez du hautematen likidorik edo zikinkeriarik."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Akatsen txostena sortzen…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Akatsen txostena partekatu nahi duzu?"</string>
@@ -1910,7 +1900,7 @@
<string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Aplikazioa Android-en bertsio zaharrago baterako sortu zenez, baliteke behar bezala ez funtzionatzea. Bilatu eguneratzerik baden, edo jarri garatzailearekin harremanetan."</string>
<string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Bilatu eguneratzeak"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Mezu berriak dituzu"</string>
- <string name="new_sms_notification_content" msgid="7002938807812083463">"Mezuak ikusteko, ireki SMS mezuen aplikazioa"</string>
+ <string name="new_sms_notification_content" msgid="7002938807812083463">"Mezuak ikusteko, ireki SMS mezuetarako aplikazioa"</string>
<string name="profile_encrypted_title" msgid="4260432497586829134">"Baliteke funtzio batzuk mugatuta egotea"</string>
<string name="profile_encrypted_detail" msgid="3700965619978314974">"Blokeatuta dago laneko profila"</string>
<string name="profile_encrypted_message" msgid="6964994232310195874">"Sakatu profila desblokeatzeko"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index d8582ff..17604d0 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"نماد اثر انگشت"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"مدیریت سختافزار face unlock"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"به برنامه امکان میدهد روشهایی را برای افزودن و حذف الگوهای چهره جهت استفاده فرابخواند."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"استفاده از سختافزار face unlock"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"به برنامه امکان میدهد از سختافزار face unlock برای احراز هویت استفاده کند"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Face unlock"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"ثبت مجدد چهره"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"برای بهبود تشخیص، لطفاً چهرهتان را دوباره ثبت کنید"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"دادههای دقیق چهره ضبط نشد. دوباره امتحان کنید."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"چهره تأیید نشد. سختافزار در دسترس نیست."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"face unlock را دوباره امتحان کنید."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"داده چهره جدید ذخیره نشد. اول داده چهره قدیمی را حذف کنید."</string>
<string name="face_error_canceled" msgid="283945501061931023">"عملیات شناسایی چهره لغو شد."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"کاربر Face unlock را لغو کرد."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"تعداد زیادی تلاش ناموفق. بعداً دوباره امتحان کنید."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"تعداد تلاشها بیشازحد مجاز است. Face unlock غیرفعال است."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"چهره تأیید نشد. دوباره امتحان کنید."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"face unlock را راهاندازی نکردید."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Face unlock در این دستگاه پشتیبانی نمیشود."</string>
<string name="face_name_template" msgid="7004562145809595384">"چهره <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"برای غیرفعال کردن «حالت مجموعه دادههای تست»، بازنشانی کارخانهای کنید."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"مایعات یا خاکروبه در درگاه USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"درگاه USB بهطور خودکار غیرفعال شده است. برای اطلاعات بیشتر، ضربه بزنید."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"میتوان از درگاه USB استفاده کرد"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"تلفن دیگر وجود مایعات یا خاکروبه را تشخیص نمیدهد."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"درحال گرفتن گزارش اشکال…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"گزارش اشکال به اشتراک گذاشته شود؟"</string>
@@ -1502,8 +1492,8 @@
<string name="websearch" msgid="4337157977400211589">"جستجوی وب"</string>
<string name="find_next" msgid="5742124618942193978">"یافتن بعدی"</string>
<string name="find_previous" msgid="2196723669388360506">"یافتن قبلی"</string>
- <string name="gpsNotifTicker" msgid="5622683912616496172">"درخواست موقعیت مکانی از <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"درخواست موقعیت مکانی"</string>
+ <string name="gpsNotifTicker" msgid="5622683912616496172">"درخواست مکان از <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"درخواست مکان"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"درخواست شده توسط <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"بله"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"نه"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 921788f..b51bb81 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Sormenjälkikuvake"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"hallinnoida Face Unlock ‑laitteistoa"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Sallii sovelluksen käyttää menetelmiä, joilla voidaan lisätä tai poistaa kasvomalleja."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"käyttää Face Unlock ‑laitteistoa"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Sallii sovelluksen käyttää Face Unlock ‑laitteistoa todennukseen"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Face Unlock"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Lisää kasvot uudelleen"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Lisää kasvosi uudelleen tunnistamisen parantamiseksi"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Tarkan kasvodatan tallennus epäonnistui. Yritä uudelleen."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Kasvoja ei voi vahvistaa. Laitteisto ei käytettäv."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Yritä käyttää Face Unlockia uudelleen."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Uutta kasvodataa ei voi tallentaa. Poista ensin vanhaa."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Kasvotoiminto peruutettu"</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Käyttäjä peruutti Face Unlockin."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Liian monta yritystä. Yritä myöhemmin uudelleen."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Liian monta yritystä. Face Unlock poistettu käytöstä."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Kasvoja ei voi vahvistaa. Yritä uudelleen."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Et ole määrittänyt Face Unlockia."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Tämä laite ei tue Face Unlockia."</string>
<string name="face_name_template" msgid="7004562145809595384">"Kasvot <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Palauta tehdasasetukset, niin voit poistaa testikehystilan käytöstä."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Nestettä tai likaa USB-portissa"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-portti poistetaan käytöstä automaattisesti. Napauta nähdäksesi lisätietoja."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB-portin käyttö on sallittu"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Puhelin ei enää havaitse nesteitä eikä likaa."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Luodaan virheraporttia…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Jaetaanko virheraportti?"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 8f99e3d..a24756b 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icône d\'empreinte digitale"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"gérer le matériel de déverrouillage par reconnaissance faciale"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Permet à l\'appli d\'employer des méthodes d\'aj. et de suppr. de modèles de reconn. visage."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"utiliser le matériel de déverrouillage par reconnaissance faciale"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Permet à l\'appli d\'utiliser du matériel de déverr. par reconn faciale pour l\'authentific."</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Déverrouillage par reconnaissance faciale"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Inscrivez votre visage à nouveau"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Pour améliorer la reconnaissance, veuillez enregistrer à nouveau votre visage"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Imposs. capt. données visage précises. Réessayez."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Imposs. de vérif. visage. Matériel non accessible."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Réessayez le déverr. par reconnaissance faciale."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Impossible de stocker de nouveaux visages. Supprimez-en un."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Opération de reconnaissance du visage annulée."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Déverr. par reconn. faciale annulé par l\'utilisateur."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Trop de tentatives. Veuillez réessayer plus tard."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Trop de tentatives. Le déverr. par reconnaissance faciale est désactivé."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Impossible de vérifier le visage. Réessayez."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Vous n\'avez pas config. le déverr. par reconn. faciale."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Cet appar. ne prend pas en charge le déverr. par reconn. faciale."</string>
<string name="face_name_template" msgid="7004562145809595384">"Visage <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Effectuez une réinitialisation pour désactiver le mode Logiciel de test."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Liquide ou débris dans le port USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Le port USB est désactivé automatiquement. Touchez ici pour en savoir plus."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Autorisation d\'utiliser le port USB"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Le téléphone ne détecte plus les liquides ni les débris."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Création d\'un rapport de bogue en cours..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Partager le rapport de bogue?"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index b55f297..e78ceda 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icône d\'empreinte digitale"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"gérer les composants de Face Unlock"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Autorise l\'appli à invoquer des méthodes pour ajouter et supprimer des modèles de visages."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"utiliser les composants de Face Unlock"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Autorise l\'application à utiliser les composants de Face Unlock pour l\'authentification"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Face Unlock"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Enregistrer à nouveau votre visage"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Pour améliorer la reconnaissance, veuillez enregistrer à nouveau votre visage"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Capture du visage impossible. Réessayez."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Imposs. valider visage. Matériel non disponible."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Réessayez d\'activer Face Unlock."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Impossible stocker nouv. visages. Veuillez en supprimer un."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Opération de reconnaissance faciale annulée."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Face Unlock annulé par l\'utilisateur."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Trop de tentatives. Réessayez plus tard."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Tentatives trop nombreuses. Désactivation de Face Unlock."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Impossible de valider votre visage. Réessayez."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Face Unlock n\'est pas configuré."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Face Unlock n\'est pas compatible avec cet appareil."</string>
<string name="face_name_template" msgid="7004562145809595384">"Visage <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Rétablissez la configuration d\'usine pour désactiver le mode Atelier de test."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Présence de liquide ou de saletés dans le port USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Le port USB est désactivé automatiquement. Appuyez sur cette notification pour en savoir plus."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Autoriser l\'utilisation du port USB"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Le téléphone ne détecte plus les liquides ni les saletés."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Création du rapport de bug…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Partager le rapport de bug ?"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 5e497a0..f740edf 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icona de impresión dixital"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"xestionar o hardware de desbloqueo facial"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Permite que a aplicación invoque métodos para engadir e eliminar modelos faciais de uso."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"utilizar o hardware de desbloqueo facial"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Permite que a aplicación utilice o hardware de desbloqueo facial para a autenticación"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Desbloqueo facial"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Volve inscribir a túa cara"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Para mellorar o recoñecemento, inscribe de novo a túa cara"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Sen datos faciais exactos. Téntao de novo."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Sen verificar a cara. Hardware non dispoñible."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Tenta utilizar o desbloqueo facial de novo."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Para gardar novos datos faciais, elimina os antigos."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Cancelouse a operación relacionada coa cara"</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"O usuario cancelou o desbloqueo facial."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Demasiados intentos. Téntao de novo máis tarde."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Demasiados intentos. Desactivouse o desbloqueo facial."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Non se puido verificar a cara. Téntao de novo."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Non configuraches o desbloqueo facial."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Este dispositivo non admite o desbloqueo facial."</string>
<string name="face_name_template" msgid="7004562145809595384">"Cara <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1367,8 +1358,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Restablece a configuración de fábrica para desactivar o modo de axente de proba."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Hai líquido ou residuos no porto USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"O porto USB desactivouse automaticamente. Toca para obter máis información."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Pódese utilizar o porto USB"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"O teléfono xa non detecta líquidos nin residuos."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Creando informe de erros…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Queres compartir o informe de erros?"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index c03db76..dc23dcd 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ફિંગરપ્રિન્ટ આયકન"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"ફેસ અનલૉકના હાર્ડવેરને મેનેજ કરો"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"ઍપને ઉપયોગ માટે ચહેરાના નમૂના ઉમેરવા અને ડિલીટ કરવાની પદ્ધતિને રદ કરવાની મંજૂરી આપે છે."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"ફેસ અનલૉક હાર્ડવેરનો ઉપયોગ કરો"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"ઍપને પ્રમાણીકરણ માટે ફેસ અનલૉકના હાર્ડવેરનો ઉપયોગ કરવાની મંજૂરી આપે છે"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"ફેસ અનલૉક"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"તમારા ચહેરાની ફરી નોંધણી કરાવો"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"ઓળખવાની પ્રક્રિયાને બહેતર બનાવવા માટે કૃપા કરીને તમારા ચહેરાની ફરી નોંધણી કરાવો"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"ચહેરાનો સચોટ ડેટા કૅપ્ચર ન થયો. ફરી પ્રયાસ કરો."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"ચહેરો ચકાસી શકાતો નથી. હાર્ડવેર ઉપલબ્ધ નથી."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"ફેસ અનલૉકને ફરી અજમાવો."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"ચહેરાનો નવો ડેટા સ્ટોર કરી શકતાં નથી. પહેલા જૂનો ડિલીટ કરો."</string>
<string name="face_error_canceled" msgid="283945501061931023">"ચહેરા સંબંધિત કાર્યવાહી રદ કરવામાં આવી છે."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"વપરાશકર્તાએ ફેસ અનલૉક રદ કર્યું."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"ઘણા બધા પ્રયત્નો. થોડા સમય પછી ફરી પ્રયાસ કરો."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"ઘણા બધા પ્રયાસો. ફેસ અનલૉક બંધ કર્યું."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"ચહેરો ચકાસી શકાતો નથી. ફરી પ્રયાસ કરો."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"તમે ફેસ અનલૉકનું સેટઅપ કર્યું નથી."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"આ ડિવાઇસ પર ફેસ અનલૉક કરવાની સુવિધા નથી."</string>
<string name="face_name_template" msgid="7004562145809595384">"ચહેરાનું <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1367,8 +1358,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"ટેસ્ટ હાર્નેસ મોડ બંધ કરવા માટે ફૅક્ટરી રીસેટ કરો."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB પોર્ટમાં પ્રવાહી કે ધૂળ"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB પોર્ટ ઑટોમૅટિક રીતે બંધ કરવામાં આવ્યો છે. વધુ જાણવા માટે ટૅપ કરો."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB પોર્ટનો ઉપયોગ કરવો યોગ્ય છે"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"ફોનને હવે કોઈ પ્રવાહી કે ધૂળ હોવાનું જણાયું નથી."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"બગ રિપોર્ટ લઈ રહ્યાં છે…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"બગ રિપોર્ટ શેર કરીએ?"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 4eeb661..4a4bdf9 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"फ़िंगरप्रिंट आइकॉन"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"\'मालिक का चेहरा पहचानकर अनलॉक\' वाला हार्डवेयर प्रबंधित करें"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"ऐप्लिकेशन को चेहरे के टेम्पलेट इस्तेमाल के तरीके जोड़ने और मिटाने की मंज़ूरी मिलती है."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"\'मालिक का चेहरा पहचानकर अनलॉक\' वाला हार्डवेयर इस्तेमाल करें"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"ऐप्लिकेशन को \'मालिक का चेहरा पहचानकर अनलॉक\' वाले हार्डवेयर के इस्तेमाल की मंज़ूरी देता है"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"मालिक का चेहरा पहचानकर अनलॉक"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"अपना चेहरा फिर से दर्ज करें"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"कृपया अपना चेहरा फिर से दर्ज करें ताकि आपको बेहतर तरीके से पहचाना जा सके"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"चेहरे से जुड़ा सटीक डेटा कैप्चर नहीं किया जा सका. फिर से कोशिश करें."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"चेहरा नहीं पहचान पा रहे. हार्डवेयर उपलब्ध नहीं है."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"\'मालिक का चेहरा पहचानकर अनलॉक\' फिर से आज़माएं."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"चेहरे का नया डेटा सेव नहीं हो सकता. कोई पुराना डेटा मिटाएं."</string>
<string name="face_error_canceled" msgid="283945501061931023">"चेहरा पहचानने की कार्रवाई रद्द की गई."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"उपयोगकर्ता ने \'मालिक का चेहरा पहचानकर अनलॉक\' रद्द की."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"कई बार कोशिश की गई. बाद में कोशिश करें."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"कई बार कोशिश की जा चुकी है. \'मालिक का चेहरा पहचानकर अनलॉक\' बंद है."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"चेहरा नहीं पहचान पा रहे. फिर से कोशिश करें."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"आपने \'मालिक का चेहरा पहचानकर अनलॉक\' सेट नहीं की है."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"इस डिवाइस पर \'मालिक का चेहरा पहचानकर अनलॉक\' काम नहीं करती है."</string>
<string name="face_name_template" msgid="7004562145809595384">"चेहरा <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"टेस्ट हार्नेस मोड बंद करने के लिए फ़ैक्ट्री रीसेट करें."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"यूएसबी पोर्ट में तरल चीज़ या कचरा है"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"यूएसबी पोर्ट अपने आप बंद हो गया है. ज़्यादा जानने के लिए टैप करें."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"यूएसबी पोर्ट का इस्तेमाल करना सुरक्षित है"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"फ़ोन अब तरल चीज़ों या कचरे की पहचान नहीं करता."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"गड़बड़ी की रिपोर्ट ली जा रही है…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"गड़बड़ी की रिपोर्ट शेयर करें?"</string>
@@ -1465,7 +1455,7 @@
<string name="chooser_wallpaper" msgid="7873476199295190279">"वॉलपेपर बदलें"</string>
<string name="notification_listener_binding_label" msgid="2014162835481906429">"सूचना को सुनने की सुविधा"</string>
<string name="vr_listener_binding_label" msgid="4316591939343607306">"VR श्रोता"</string>
- <string name="condition_provider_service_binding_label" msgid="1321343352906524564">"स्थिति प्रदाता"</string>
+ <string name="condition_provider_service_binding_label" msgid="1321343352906524564">"स्थिति देने वाली"</string>
<string name="notification_ranker_binding_label" msgid="774540592299064747">"सूचना रैंकर सेवा"</string>
<string name="vpn_title" msgid="19615213552042827">"VPN सक्रिय"</string>
<string name="vpn_title_long" msgid="6400714798049252294">"VPN को <xliff:g id="APP">%s</xliff:g> द्वारा सक्रिय किया गया है"</string>
@@ -1503,7 +1493,7 @@
<string name="find_next" msgid="5742124618942193978">"आगे ढूंढें"</string>
<string name="find_previous" msgid="2196723669388360506">"पिछला ढूंढें"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"<xliff:g id="NAME">%s</xliff:g> ने जगह का अनुरोध किया गया है"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"जगह का अनुरोध किया जा रहा है"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"जगह की जानकारी मांगी जा रही है"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"<xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>) द्वारा अनुरोधित"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"हां"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"नहीं"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index eb09104..d68895f 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -552,15 +552,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona otiska prsta"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"Upravljajte hardverom za otključavanje licem"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Aplikaciji omogućuje pozivanje načina za dodavanje i brisanje predložaka lica za upotrebu."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"Koristiti hardver za otključavanje licem"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Aplikaciji omogućuje upotrebu hardvera za otključavanje licem radi autentifikacije"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Otključavanje licem"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Ponovo registrirajte svoje lice"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Za poboljšanje prepoznavanja ponovo registrirajte svoje lice"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Podaci o licu nisu točni. Pokušajte ponovo."</string>
@@ -586,20 +582,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Lice nije potvrđeno. Hardver nije dostupan."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Ponovo pokušajte otključavanje licem."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Podaci o novom licu nisu pohranjeni. Izbrišite neko staro."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Otkazana je radnja s licem."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Otključavanje licem otkazao je korisnik."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Previše pokušaja. Pokušajte ponovo kasnije."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Previše pokušaja. Onemogućeno otključavanje licem"</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Lice nije potvrđeno. Pokušajte ponovo."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Niste postavili otključavanje licem"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Otključavanje licem nije podržano na ovom uređaju."</string>
<string name="face_name_template" msgid="7004562145809595384">"Lice <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1388,8 +1379,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Vratite na tvorničke postavke da biste onemogućili način testnog okvira."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Tekućina ili prljavština u USB priključku"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB priključak automatski je onemogućen. Dodirnite da biste saznali više."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Možete koristiti USB priključak"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Telefon više ne otkriva tekućinu ili prljavštinu."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Izrada izvješća o programskoj pogrešci…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Želite li podijeliti izvješće o programskoj pogrešci?"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 608a121..98c93f6 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ujjlenyomat ikon"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"arcalapú feloldásra szolgáló hardver kezelése"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Engedélyezi, hogy az alkalmazás arcsablon-hozzáadási és -törlési metódusokat hívjon."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"arcalapú feloldásra szolgáló hardver használata"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Engedélyezi, hogy az alkalmazás hitelesítésre használja az arcalapú feloldás hardverét"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Arcalapú feloldás"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Rögzítsen újra képet az arcáról"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"A felismerés javítása érdekében rögzítsen újra az arcáról készített képet"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Sikertelen az arc pontos rögzítése. Próbálja újra."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Sikertelen arcellenőrzés. A hardver nem érhető el."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Próbálja újra az arcalapú feloldást."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Nem tárolhatók újabb arcadatok. Törölje valamelyik arcot."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Az arccal kapcsolatos művelet törölve."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Az arcalapú feloldást megszakította a felhasználó."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Túl sok próbálkozás. Próbálja újra később."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Túl sok próbálkozás. Az arcalapú feloldás letiltva."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Nem sikerült ellenőrizni az arcát. Próbálja újra."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Nem állította be az arcalapú feloldást."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Az eszköz nem támogatja az arcalapú feloldást"</string>
<string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g> arc"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"A Tesztelési alapkörnyezet mód kikapcsolásához állítsa vissza a gyári beállításokat."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Folyadék vagy szennyeződés az USB-portban"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-port automatikusan letiltva. Koppintson, ha további információra van szüksége."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Az USB-port rendben használható"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"A telefon már nem észlel folyadékot vagy szennyeződést."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Hibajelentés készítése…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Megosztja a hibajelentést?"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index d99afcb9..5008bda 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -280,7 +280,7 @@
<string name="permgrouprequest_contacts" msgid="6032805601881764300">"Թույլատրե՞լ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> հավելվածին օգտագործել ձեր կոնտակտները"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"Տեղորոշում"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"տեղորոշել այս սարքը"</string>
- <string name="permgrouprequest_location" msgid="3788275734953323491">"Թույլատրե՞լ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>-ին օգտագործել այս սարքի տեղադրության տվյալները"</string>
+ <string name="permgrouprequest_location" msgid="3788275734953323491">"Թույլատրե՞լ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> հավելվածին օգտագործել այս սարքի տեղադրության տվյալները"</string>
<string name="permgrouprequestdetail_location" msgid="1347189607421252902">"Տեղադրության տվյալները հասանելի կլինեն հավելվածին, միայն երբ այն օգտագործելիս լինեք"</string>
<string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"<b>Միշտ</b> հասանելի դարձնե՞լ <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> հավելվածին ձեր սարքի տեղադրությունը"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"Տեղադրության տվյալները հասանելի կլինեն հավելվածին, միայն երբ այն օգտագործելիս լինեք"</string>
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Մատնահետքի պատկերակ"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"կառավարել դեմքով ապակողպման համար սարքը"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Հավելվածին թույլ է տալիս ավելացնել և հեռացնել դեմքի նմուշներ:"</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"օգտագործել դեմքով ապակողպման համար սարքը"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Թույլ է տալիս հավելվածին օգտագործել դեմքով ապակողպման համար նախատեսված սարքը"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Դեմքով ապակողպում"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Նորից գրանցեք ձեր դեմքը"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Ճանաչումը լավացնելու համար նորից գրանցեք ձեր դեմքը"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Չհաջողվեց գրանցել դեմքի ճշգրիտ տվյալները։ Կրկնեք։"</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Չհաջողվեց հաստատել դեմքը։ Սարքն անհասանելի է:"</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Նորից փորձեք դեմքով ապակողպումը։"</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Չհաջողվեց պահել նոր դեմքը։ Ջնջեք հին տարբերակը։"</string>
<string name="face_error_canceled" msgid="283945501061931023">"Դեմքի ճանաչումը չեղարկվել է։"</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Դեմքով ապակողմումը չեղարկվել է օգտատիրոջ կողմից:"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Չափից շատ փորձեր եք կատարել: Փորձեք ավելի ուշ:"</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Չափազանց շատ փորձեր են արվել։ Դեմքով ապակողպումն անջատված է:"</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Չհաջողվեց հաստատել դեմքը։ Նորից փորձեք։"</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Դուք չեք կարգավորել դեմքով ապակողպումը:"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Դեմքով ապակողպումն այս սարքում չի աջակցվում"</string>
<string name="face_name_template" msgid="7004562145809595384">"Դեմք <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Թեստային ռեժիմն անջատելու համար զրոյացրեք կարգավորումները։"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB միացքում ջուր կամ աղտ է հայտնաբերվել"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB միացքն ավտոմատ անջատվել է: Հպեք՝ ավելին իմանալու համար:"</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB միացքը կարող է օգտագործվել"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Հեռախոսում ջուր կամ աղտ չի հայտնաբերվել:"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Վրիպակի զեկույցի ստեղծում…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Տրամադրե՞լ վրիպակի զեկույցը:"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index a3cc652..0908926 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -584,7 +584,7 @@
<string name="face_error_canceled" msgid="283945501061931023">"Pemrosesan wajah dibatalkan."</string>
<string name="face_error_user_canceled" msgid="5317030072349668946">"Face unlock dibatalkan oleh pengguna."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Terlalu banyak percobaan. Coba lagi nanti."</string>
- <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Terlalu banyak percobaan. Face unlock dinonaktifkan."</string>
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Terlalu banyak gagal. Face unlock dinonaktifkan."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Tidak dapat memverifikasi wajah. Coba lagi."</string>
<string name="face_error_not_enrolled" msgid="4016937174832839540">"Anda belum menyiapkan face unlock."</string>
<string name="face_error_hw_not_present" msgid="8302690289757559738">"Face unlock tidak didukung di perangkat ini."</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 00637a4..faf065e 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Fingrafaratákn"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"stjórna vélbúnaði andlitsopnunar"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Leyfir forritinu að beita aðferðum til að bæta við og eyða andlitssniðmátum til notkunar."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"nota vélbúnað andlitsopnunar"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Leyfir forritinu að nota andlitsopnunarvélbúnað til auðkenningar"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Andlitsopnun"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Skráðu andlitið þitt aftur"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Skráðu andlitið þitt til að bæta kennsl"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Nákvæm andlitsgögn fengust ekki. Reyndu aftur."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Andlit ekki staðfest. Vélbúnaður er ekki tiltækur."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Prófaðu andlitsopnun aftur."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Ekki er hægt að vista ný andlitsgögn. Eyddu gömlu fyrst."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Hætt við andlitsgreiningu."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Notandi hætti við andlitsopnun."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Of margar tilraunir. Reyndu aftur síðar."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Of margar tilraunir. Slökkt á andlitsopnun."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Ekki tókst að staðfesta andlit. Reyndu aftur."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Þú hefur ekki sett upp andlitsopnun."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Andlitsopnun er ekki studd í þessu tæki."</string>
<string name="face_name_template" msgid="7004562145809595384">"Andlit <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1367,8 +1358,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Núllstilltu til að slökkva á stillingu prófunarvangs."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Vökvi eða óhreinindi í USB-tengi"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-tengi er gert óvirkt sjálfkrafa. Ýttu til að fá frekari upplýsingar."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Óhætt að nota USB-tengi"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Síminn greinir ekki lengur vökva eða óhreinindi"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Tekur við villutilkynningu…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Deila villutilkynningu?"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 96d44c6..e54d305 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1492,8 +1492,8 @@
<string name="websearch" msgid="4337157977400211589">"Ricerca Web"</string>
<string name="find_next" msgid="5742124618942193978">"Trova successivo"</string>
<string name="find_previous" msgid="2196723669388360506">"Trova precedente"</string>
- <string name="gpsNotifTicker" msgid="5622683912616496172">"Richiesta posizione da <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"Richiesta posizione"</string>
+ <string name="gpsNotifTicker" msgid="5622683912616496172">"Richiesta di posizione da <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"Richiesta di posizione"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"Richiesto da <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"Sì"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"No"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 78085c7..3bec6d0 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -555,15 +555,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"סמל טביעת אצבע"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"ניהול החומרה לשחרור נעילה על ידי זיהוי פנים"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"מאפשרת לאפליקציה להפעיל שיטות להוספה ומחיקה של תבניות פנים שבהן ייעשה שימוש."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"שימוש בחומרה לשחרור נעילה על ידי זיהוי פנים"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"מאפשרת לאפליקציה להשתמש בחומרה לשחרור נעילה על ידי זיהוי פנים לצורך אימות"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"שחרור נעילה על ידי זיהוי פנים"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"יש לבצע רישום מחדש של הפנים שלך"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"לשיפור הזיהוי יש לבצע רישום מחדש של הפנים שלך"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"לא ניתן היה לקלוט את הפנים במדויק. יש לנסות שוב."</string>
@@ -589,20 +585,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"לא ניתן לאמת את הפנים. החומרה לא זמינה."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"יש לנסות שוב את שחרור הנעילה על ידי זיהוי פנים."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"לא ניתן לאחסן נתוני פנים. תחילה יש למחוק פנים ישנים."</string>
<string name="face_error_canceled" msgid="283945501061931023">"פעולת הפנים בוטלה."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"שחרור הנעילה על ידי זיהוי פנים בוטל על ידי המשתמש."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"יותר מדי ניסיונות. יש לנסות שוב מאוחר יותר."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"יותר מדי ניסיונות. שחרור נעילה על ידי זיהוי פנים מושבת."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"לא ניתן לאמת את הפנים. יש לנסות שוב."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"לא הגדרת שחרור נעילה על ידי זיהוי פנים."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"המכשיר הזה לא תומך בשחרור נעילה על ידי זיהוי פנים."</string>
<string name="face_name_template" msgid="7004562145809595384">"פנים <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1410,8 +1401,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"כדי להשבית את מצב מסגרת בדיקה צריך לאפס להגדרות היצרן."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"יש נוזלים או חלקיקים ביציאת ה-USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"יציאת ה-USB הושבתה באופן אוטומטי. יש להקיש לקבלת מידע נוסף."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"ניתן להשתמש ביציאת ה-USB"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"הטלפון לא מזהה יותר נוזלים וחלקיקים."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"עיבוד דוח על באג..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"האם לשתף דוח על באג?"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index e71bc0a..439c64e 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"指紋アイコン"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"フェイスアンロック ハードウェアの管理"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"使用する顔テンプレートの追加や削除を行うメソッドの呼び出しをアプリに許可します。"</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"フェイスアンロック ハードウェアの使用"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"フェイスアンロック ハードウェアを認証に使用することをアプリに許可します"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"フェイスアンロック"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"顔の再登録"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"認識を改善するには、顔を再登録してください"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"顔を認識できませんでした。もう一度お試しください。"</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"顔を確認できません。ハードウェアを利用できません。"</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"フェイスアンロックをもう一度お試しください。"</string>
<string name="face_error_no_space" msgid="2712120617457553825">"新しい顔データを保存できません。古いデータを削除してください。"</string>
<string name="face_error_canceled" msgid="283945501061931023">"顔の操作をキャンセルしました。"</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"フェイスアンロックはユーザーによりキャンセルされました。"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"試行回数の上限です。後でもう一度お試しください。"</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"試行回数が上限を超えました。フェイスアンロックを無効にしました。"</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"顔を確認できません。もう一度お試しください。"</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"フェイスアンロックを設定していません。"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"このデバイスでは、フェイスアンロックはご利用いただけません。"</string>
<string name="face_name_template" msgid="7004562145809595384">"顔 <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"出荷時設定にリセットしてテストハーネス モードを無効にしてください。"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB ポート内の液体やゴミ"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB ポートが自動的に無効になりました。タップして詳細をご確認ください。"</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB ポートを安全に使用できます"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"液体やゴミは検出されなくなりました。"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"バグレポートを取得しています…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"バグレポートを共有しますか?"</string>
@@ -1503,7 +1493,7 @@
<string name="find_next" msgid="5742124618942193978">"次を検索"</string>
<string name="find_previous" msgid="2196723669388360506">"前を検索"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"<xliff:g id="NAME">%s</xliff:g>さんからの現在地情報リクエスト"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"現在地情報へのアクセス許可"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"現在地情報リクエスト"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"<xliff:g id="NAME">%1$s</xliff:g>さん(<xliff:g id="SERVICE">%2$s</xliff:g>)からのリクエスト"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"はい"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"いいえ"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index d9ae8ea..2856d03 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"თითის ანაბეჭდის ხატულა"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"სახით განბლოკვის აპარატურის მართვა"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"საშუალებას აძლევს აპს, დაამატოს და წაშალოს სახეების შაბლონები."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"სახით განბლოკვის აპარატურის გამოყენება"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"საშუალებას აძლევს აპს, ამოცნობისთვის გამოიყენოს სახით განბლოკვის აპარატურა"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"განბლოკვა სახით"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"დაარეგისტრირეთ თქვენი სახე ხელახლა"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"ამოცნობის გასაუმჯობესებლად, გთხოვთ, ხელახლა დაარეგისტრიროთ თქვენი სახე"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"სახის ზუსტი მონაცემები არ აღიბეჭდა. ცადეთ ხელახლა."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"სახე ვერ დასტურდება. აპარატი მიუწვდომელია."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"ცადეთ ხელახლა სახით განბლოკვა."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"სახის ახალი მონაცემები ვერ ინახება. ჯერ ძველი წაშალეთ."</string>
<string name="face_error_canceled" msgid="283945501061931023">"სახის ამოცნობა გაუქმდა."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"განბლოკვა სახით გაუქმდა მომხმარებლის მიერ."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"დაფიქსირდა ბევრი მცდელობა. ცადეთ მოგვიანებით."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"მეტისმეტად ბევრი მცდელობა იყო. სახით განბლოკვა გათიშულია."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"სახის დადასტურება ვერ ხერხდება. ცადეთ ხელახლა."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"თქვენ არ დაგიყენებიათ სახით განბლოკვა."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"განბლოკვა სახით ამ მოწყობილობაზე მხარდაჭერილი არ არის."</string>
<string name="face_name_template" msgid="7004562145809595384">"სახე <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"დააბრუნეთ ქარხნული პარამეტრები „გარემო ტესტირებისთვის“ რეჟიმის გასათიშად."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB პორტში აღმოჩენილია სითხე ან ჭუჭყი"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB პორტი ავტომატურად გათიშულია. შეეხეთ დამატებითი ინფორმაციისთვის."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"თანხმობა USB პორტის გამოყენებაზე"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"ტელეფონი აღარ აღმოაჩენს სითხეს ან ჭუჭყს."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"მიმდინარეობს ხარვეზის შესახებ ანგარიშის შექმნა…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"გსურთ ხარვეზის შესახებ ანგარიშის გაზიარება?"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index cb43ba0..09d2c9b 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Саусақ ізі белгішесі"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"Face Unlock жабдығын басқару"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Қолданбаға пайдаланатын бет үлгілерін енгізу және жою әдістерін шақыруға мүмкіндік береді."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"Face Unlock жабдығын пайдалану"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Аутентификациялау үшін қолданбаға Face Unlock жабдығын пайдалануға рұқсат береді."</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Face Unlock"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Бетті қайта тіркеу"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Құрылғы жүзіңізді жақсырақ тануы үшін, бетіңізді қайта тіркеңіз."</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Бет деректері дұрыс алынбады. Әрекетті қайталаңыз."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Бетті тану мүмкін емес. Жабдық қолжетімді емес."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Face Unlock функциясын қайта қолданып көріңіз."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Жаңа бетті сақтау мүмкін емес. Алдымен ескісін жойыңыз."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Бетті танудан бас тартылды."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Пайдаланушы Face Unlock функциясынан бас тартты."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Тым көп әрекет жасалды. Кейінірек қайталаңыз."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Тым көп әрекет жасалды. Face Unlock функциясы өшірілді."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Бетті тану мүмкін емес. Әрекетті қайталаңыз."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Face Unlock реттелмеді."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Бұл құрылғыда Face Unlock функциясы істемейді."</string>
<string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g> беті"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1367,8 +1358,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Сынақ бағдарламасы режимін өшіру үшін зауыттық күйіне қайтарыңыз."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB портына сұйықтық немесе қоқыс кірді"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB порты автоматты түрде өшірілді. Толығырақ ақпарат алу үшін түртіңіз."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB портын пайдалана беруге болады"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Телефон бұдан былай сұйықтықты немесе қоқысты анықтамайды."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Қате туралы есеп алынуда…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Қате туралы есепті бөлісу керек пе?"</string>
@@ -1504,7 +1494,7 @@
<string name="find_next" msgid="5742124618942193978">"Келесіні табу"</string>
<string name="find_previous" msgid="2196723669388360506">"Алдыңғыны табу"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"Орын туралы өтініші жіберген <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"Орын туралы өтініш"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"Орын туралы сұрау"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"Өтініш жіберген <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"Иә"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"Жоқ"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index f1a573e..d70469a 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"រូបតំណាងស្នាមម្រាមដៃ"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"គ្រប់គ្រងហាតវែរដោះសោតាមទម្រង់មុខ"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"អនុញ្ញាតឱ្យកម្មវិធីប្រើវិធីសាស្ត្រដើម្បីបញ្ចូល និងលុបទម្រង់គំរូផ្ទៃមុខសម្រាប់ប្រើប្រាស់។"</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"ប្រើហាតវែរដោះសោតាមទម្រង់មុខ"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"អនុញ្ញាតឱ្យកម្មវិធីប្រើហាតវែរដោះសោតាមទម្រង់មុខសម្រាប់ការផ្ទៀងផ្ទាត់"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"ការដោះសោតាមទម្រង់មុខ"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"ស្កេនបញ្ចូលមុខរបស់អ្នកម្ដងទៀត"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"ដើម្បីធ្វើឱ្យការសម្គាល់មុខប្រសើរជាងមុន សូមស្កេនបញ្ចូលមុខរបស់អ្នកម្ដងទៀត"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"មិនអាចថតទិន្នន័យទម្រង់មុខបានត្រឹមត្រូវទេ។ សូមព្យាយាមម្ដងទៀត។"</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"មិនអាចផ្ទៀងផ្ទាត់មុខបានទេ។ មិនមានហាតវែរទេ។"</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"សាកល្បងដោះសោតាមទម្រង់មុខម្ដងទៀត។"</string>
<string name="face_error_no_space" msgid="2712120617457553825">"មិនអាចផ្ទុកទិន្នន័យទម្រង់មុខថ្មីបានទេ។ សូមលុបទិន្នន័យទម្រង់មុខចាស់ជាមុនសិន។"</string>
<string name="face_error_canceled" msgid="283945501061931023">"បានបោះបង់ប្រតិបត្តិការចាប់ផ្ទៃមុខ។"</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"អ្នកប្រើប្រាស់បានបោះបង់ការដោះសោតាមទម្រង់មុខ។"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"ព្យាយាមចូលច្រើនពេកហើយ។ សូមព្យាយាមម្តងទៀតពេលក្រោយ។"</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"ការដោះសោតាមទម្រង់មុខត្រូវបានបិទ ដោយសារព្យាយាមច្រើនដងពេក។"</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"មិនអាចផ្ទៀងផ្ទាត់មុខបានទេ។ សូមព្យាយាមម្ដងទៀត។"</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"អ្នកមិនទាន់រៀបចំការដោះសោតាមទម្រង់មុខនៅឡើយទេ។"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"មិនអាចប្រើការដោះសោតាមទម្រង់មុខនៅលើឧបករណ៍នេះបានទេ។"</string>
<string name="face_name_template" msgid="7004562145809595384">"ផ្ទៃមុខទី <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1368,8 +1359,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"ធ្វើការកំណត់ដូចដើមឡើងវិញ ដើម្បីបិទមុខងារប្រមូលធ្វើតេស្ត។"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"មានទឹក ឬកម្ទេចផ្សេងៗនៅក្នុងរន្ធ USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"រន្ធ USB ត្រូវបានបិទដោយស្វ័យប្រវត្តិ។ សូមចុចដើម្បីស្វែងយល់បន្ថែម។"</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"អាចប្រើរន្ធ USB បាន"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"ទូរសព្ទរកមិនឃើញទឹក ឬកម្ទេចផ្សេងៗទេ។"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"កំពុងទទួលយករបាយការណ៍កំហុស…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"ចែករំលែករបាយការណ៍កំហុសឬ?"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 8bdb19d..65ccadb 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"지문 아이콘"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"얼굴인식 잠금해제 하드웨어 관리"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"사용할 얼굴 템플릿의 추가 및 삭제 메서드를 앱에서 호출하도록 허용합니다."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"얼굴인식 잠금해제 하드웨어 사용"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"앱에서 얼굴인식 잠금해제 하드웨어를 인증에 사용하도록 허용합니다."</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"얼굴인식 잠금해제"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"얼굴 재등록 필요"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"인식률을 개선하려면 얼굴을 다시 등록하세요."</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"정확한 얼굴 데이터를 캡처하지 못했습니다. 다시 시도하세요."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"얼굴을 확인할 수 없습니다. 하드웨어를 사용할 수 없습니다."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"얼굴인식 잠금해제를 다시 시도해 주세요."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"새 얼굴 데이터를 저장할 수 없습니다. 먼저 기존 얼굴 데이터를 삭제하세요."</string>
<string name="face_error_canceled" msgid="283945501061931023">"얼굴 인식 작업이 취소되었습니다."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"사용자가 얼굴인식 잠금해제를 취소했습니다."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"시도 횟수가 너무 많습니다. 나중에 다시 시도하세요."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"시도 횟수가 너무 많습니다. 얼굴인식 잠금해제가 사용 중지되었습니다."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"얼굴을 확인할 수 없습니다. 다시 시도하세요."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"얼굴인식 잠금해제를 설정하지 않았습니다."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"이 기기에서는 얼굴인식 잠금해제가 지원되지 않습니다."</string>
<string name="face_name_template" msgid="7004562145809595384">"얼굴 <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"테스트 하네스 모드를 사용 중지하려면 초기화하세요."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB 포트에서 액체 또는 이물질 감지됨"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB 포트가 자동으로 사용 중지되었습니다. 자세한 내용을 보려면 탭하세요."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB 포트를 사용해도 좋음"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"휴대전화에서 액체나 이물질이 더 이상 감지되지 않습니다."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"버그 보고서 가져오는 중..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"버그 보고서를 공유하시겠습니까?"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 4d1d0f4..77176802 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Манжа изинин сүрөтчөсү"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"жүзүнөн таануу функциясынын аппараттык камсыздоосун башкаруу"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Колдонмого пайдалануу үчүн жүздүн үлгүлөрүн кошуу жана жок кылуу мүмкүндүгүн берет."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"аппараттык камсыздоо үчүн жүзүнөн таанууну колдонуу"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Колдонмо аныктыкты текшерүүдө Жүзүнөн таануу функциясынын аппараттык камсыздоосун колдонот"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Жүзүнөн таануу"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Жүзүңүздү кайра таанытыңыз."</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Мыкты таануу үчүн, жүзүңүздү кайра таанытыңыз"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Жүзүңүз жакшы тартылган жок. Кайра аракет кылыңыз."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Жүз ырасталбай жатат. Аппараттык камсыздоо жеткиликсиз."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Жүзүнөн таануу функциясын кайра текшерип көрүңүз."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Жаңы жүздү сактоо мүмкүн эмес. Адегенде эскисин өчүрүңүз."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Жүздүн аныктыгын текшерүү жокко чыгарылды."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Жүзүнөн таануу функциясын колдонуучу өчүрүп салды."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Өтө көп жолу аракет жасадыңыз. Кийинчерээк кайра аракет кылыңыз."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Өтө көп жолу аракет кылдыңыз. Жүзүнөн таануу функциясы өчүрүлдү."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Жүз ырасталбай жатат. Кайра аракет кылыңыз."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Жүзүнөн таануу функциясын жөндөй элексиз."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Жүзүнөн таануу функциясы бул түзмөктө иштебейт."</string>
<string name="face_name_template" msgid="7004562145809595384">"Жүз <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1368,8 +1359,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Сыноо программасынын режимин өчүрүү үчүн, баштапкы жөндөөлөргө кайтарыңыз."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB портунда суюктук же урандылар бар"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB порт автоматтык түрдө өчүрүлдү. Кененирээк маалымат алуу үчүн таптап коюңуз."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB портун колдонууга болот"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Телефон суюктук менен урандыларды аныктаган жок."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Мүчүлүштүк тууралуу кабар алынууда…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Мүчүлүштүк тууралуу баяндама бөлүшүлсүнбү?"</string>
@@ -1505,7 +1495,7 @@
<string name="find_next" msgid="5742124618942193978">"Кийинкиси"</string>
<string name="find_previous" msgid="2196723669388360506">"Мурункусу"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"<xliff:g id="NAME">%s</xliff:g> колдонуучусу жайгашкан жердин маалыматын сурады"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"Жайгашкан жердин маалыматын суроо"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"Каякта экенин билүү"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"<xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>) сурады"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"Ооба"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"Жок"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index bfc999e..fd614f3 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ໄອຄອນລາຍນິ້ວມື"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"ຈັດການຮາດແວປົດລັອກດ້ວຍໜ້າ"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"ອະນຸຍາດໃຫ້ແອັບເປີດວິທີການຕ່າງໆເພື່ອເພີ່ມ ແລະ ລຶບແມ່ແບບໃບໜ້າສຳລັບການນຳໃຊ້."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"ໃຊ້ຮາດແວການປົດລັອກໃບໜ້າ"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"ອະນຸຍາດໃຫ້ແອັບໃຊ້ຮາດແວການປົດລັອກດ້ວຍໜ້າເພື່ອພິສູດຢືນຢັນ"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"ປົດລັອກດ້ວຍໜ້າ"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"ລົງທະບຽນໃບໜ້າຂອງທ່ານຄືນໃໝ່"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"ເພື່ອປັບປຸງການຈຳແນກ, ກະລຸນາລົງທະບຽນໃບໜ້າຂອງທ່ານຄືນໃໝ່."</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"ບໍ່ສາມາດບັນທຶກຂໍ້ມູນໃບໜ້າທີ່ຖືກຕ້ອງໄດ້. ກະລຸນາລອງໃໝ່."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"ບໍ່ສາມາດຢັ້ງຢືນໃບໜ້າໄດ້. ບໍ່ມີຮາດແວໃຫ້ໃຊ້."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"ລອງປົດລັອກດ້ວຍໜ້າອີກເທື່ອໜຶ່ງ."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"ບໍ່ສາມາດບັນທຶກຂໍ້ມູນໃບໜ້າໃໝ່ໄດ້. ກະລຸນາລຶບຂໍ້ມູນເກົ່າອອກກ່ອນ."</string>
<string name="face_error_canceled" msgid="283945501061931023">"ຍົກເລີກການດຳເນີນການກັບໃບໜ້າແລ້ວ."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"ຜູ້ໃຊ້ຍົກເລີກການປົດລັອກດ້ວຍໜ້າແລ້ວ."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"ມີຄວາມພະຍາຍາມຫຼາຍຄັ້ງເກີນໄປ. ກະລຸນາລອງໃໝ່ໃນພາຍຫຼັງ."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"ພະຍາຍາມຫຼາຍເທື່ອເກີນໄປ. ປິດນຳໃຊ້ການປົດລັອກດ້ວຍໜ້າແລ້ວ."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"ບໍ່ສາມາດຢັ້ງຢືນໃບໜ້າໄດ້. ກະລຸນາລອງໃໝ່."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"ທ່ານຍັງບໍ່ໄດ້ຕັ້ງການປົດລັອກດ້ວຍໜ້າເທື່ອ."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"ບໍ່ຮອງຮັບການປົດລັອກດ້ວຍໜ້າຢູ່ອຸປະກອນນີ້."</string>
<string name="face_name_template" msgid="7004562145809595384">"ໃບໜ້າ <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"ດຳເນີນການຣີເຊັດເປັນຄ່າຈາກໂຮງງານເພື່ອປິດການນຳໃຊ້ໂໝດ Test Harness."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"ມີຂອງແຫລວ ຫຼື ເສດດິນໃນຜອດ USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"ປິດຜອດ USB ໂດຍອັດຕະໂນມັດແລ້ວ. ແຕະເພື່ອສຶກສາເພີ່ມເຕີມ."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"ສາມາດໃຊ້ຜອດ USB ໄດ້"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"ໂທລະສັບຈະບໍ່ກວດຫາຂອງແຫລວ ຫຼື ເສດດິນອີກຕໍ່ໄປ."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"ກຳລັງຂໍລາຍງານຂໍ້ຜິດພາດ…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"ແບ່ງປັນລາຍງານບັນຫາບໍ?"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 3deae13..18ecae4 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -555,15 +555,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Piršto antspaudo piktograma"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"tvarkyti Atrakinimo pagal veidą aparatinę įrangą"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Programai leidžiama aktyv. metodus, norint pridėti ir ištrinti naudojamus veidų šablonus."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"naudoti Atrakinimo pagal veidą aparatinę įrangą"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Programai leidžiama naudoti Atrakinimo pagal veidą aparatinę įrangą tapatybei nustatyti"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Atrakinimas pagal veidą"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Pakartotinis veido registravimas"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Kad patobulintumėte atpažinimą, iš naujo užregistruokite veidą"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Neužfiks. tikslūs veido duom. Bandykite dar kartą."</string>
@@ -589,20 +585,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Nepavyko patv. veido. Aparatinė įranga negalima."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Band. naudoti Atrakinimą pagal veidą dar kartą."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Nepavyko išs. naujų veido duomenų. Pirm. ištrinkite senus."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Veido atpažinimo operacija atšaukta."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Atrakinimą pagal veidą atšaukė naudotojas."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Per daug bandymų. Vėliau bandykite dar kartą."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Per daug bandymų. Atrakinimas pagal veidą išjungtas."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Nepavyko patvirtinti veido. Bandykite dar kartą."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Nenustatėte Atrakinimo pagal veidą."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Atrakinimas pagal veidą šiame įrenginyje nepalaikomas."</string>
<string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g> veidas"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1410,8 +1401,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Atkurkite gamyklinius duomenis, kad išjungtumėte testavimo sistemos režimą."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB prievade yra skysčių ar smulkių dalelių"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB prievadas automatiškai išjungtas. Palieskite, kad sužinotumėte daugiau."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Saugu naudoti USB prievadą"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Telefonas nebeaptinka skysčių ar smulkių dalelių."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Pateikiamas pranešimas apie riktą…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Bendrinti pranešimą apie riktą?"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 77ce855..11abc5f 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -552,15 +552,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Pirksta nospieduma ikona"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"pārvaldīt aparatūru, kas paredzēta autorizācijai pēc sejas"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Atļauj lietotnei izsaukt metodes izmantojamo sejas veidņu pievienošanai un dzēšanai."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"lietot aparatūru, kas paredzēta autorizācijai pēc sejas"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Atļauj lietotnei izmantot autentificēšanai aparatūru, ar ko veic autorizāciju pēc sejas"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Autorizācija pēc sejas"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Atkārtoti reģistrējiet seju"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Lai uzlabotu atpazīšanu, lūdzu, atkārtoti reģistrējiet savu seju"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Neizdevās tvert sejas datus. Mēģiniet vēlreiz."</string>
@@ -586,20 +582,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Nevar verificēt seju. Aparatūra nav pieejama."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Vēlreiz mēģiniet veikt autorizāciju pēc sejas."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Nevar saglabāt jaunās sejas datus. Dzēsiet kādu no vecajām."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Darbība ar sejas datiem atcelta."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Lietotājs atcēla autorizāciju pēc sejas."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Pārāk daudz mēģinājumu. Vēlāk mēģiniet vēlreiz."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Pārāk daudz mēģinājumu. Autorizācija pēc sejas ir atspējota."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Nevar verificēt seju. Mēģiniet vēlreiz."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Autorizācija pēc sejas nav iestatīta."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Autorizācija pēc sejas šajā ierīcē netiek atbalstīta"</string>
<string name="face_name_template" msgid="7004562145809595384">"Seja <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1388,8 +1379,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Lai atspējotu drošības pārbaudes režīmu, veiciet rūpnīcas datu atiestatīšanu."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB pieslēgvietā ir šķidrums vai daļiņas"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB pieslēgvieta ir automātiski atspējota. Pieskarieties, lai uzzinātu vairāk."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB pieslēgvietu drīkst izmantot"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Tālrunī vairs netiek konstatēts šķidrums vai gruži."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Notiek kļūdas pārskata izveide…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Vai kopīgot kļūdas pārskatu?"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 0d085c8..d0f04c2 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Икона за отпечатоци"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"управува со хардвер за „Отклучување со лик“"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Дозволува апликац. да повика начини за додавање и бришење шаблони на лице за користење."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"користи хардвер за „Отклучување со лик“"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Дозволува апликацијата да користи хардвер за „Отклучување со лик“ за проверка"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Отклучување со лик"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Повторно регистрирајте го ликот"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"За да се подобри препознавањето, повторно регистрирајте го ликот"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Не се сними прецизна слика. Обидете се повторно."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Ликот не може да се потврди. Хардвер - недостапен."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Пробајте „Отклучување со лик“ повторно."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Не зачувува податоци за нов лик. Прво избришете стар."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Операцијата со лице се откажа."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"„Отклучувањето со лик“ е откажано од корисникот."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Премногу обиди. Обидете се повторно подоцна."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Премногу обиди. „Отклучувањето со лик“ е оневозможено."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Ликот не може да се потврди. Обидете се повторно."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Не сте поставиле „Отклучување со лик“."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"„Отклучувањето со лик“ не е поддржано на уредов."</string>
<string name="face_name_template" msgid="7004562145809595384">"Лице <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1367,8 +1358,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Извршете фабричко ресетирање за да го оневозможите режимот на рамка за тестирање."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Течност или нечистотија во USB-портата"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-портата е автоматски оневозможена. Допрете за да дознаете повеќе."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Во ред е да се користи USB-порта"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Телефонот веќе не открива течност или нечистотија."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Се зема извештајот за грешки…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Да се сподели извештајот за грешки?"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index c5d8d4e..644ca2f 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"വിരലടയാള ഐക്കൺ"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് ഹാർഡ്വെയർ മാനേജ് ചെയ്യുക"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"ഉപയോഗിക്കാനായി, മുഖത്തിന്റെ ടെംപ്ലേറ്റുകൾ ചേർക്കാനും ഇല്ലാതാക്കാനുമുള്ള രീതികൾ അഭ്യർത്ഥിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് ഹാർഡ്വെയർ ഉപയോഗിക്കുക"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"പരിശോധിച്ചുറപ്പിക്കാൻ മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് ഹാർഡ്വെയർ ഉപയോഗിക്കാൻ അനുവദിക്കുന്നു"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക്"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"നിങ്ങളുടെ മുഖം വീണ്ടും എൻറോൾ ചെയ്യൂ"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"തിരിച്ചറിയൽ മെച്ചപ്പെടുത്താൻ, നിങ്ങളുടെ മുഖം ദയവായി വീണ്ടും എൻറോൾ ചെയ്യൂ"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"കൃത്യ മുഖ ഡാറ്റ എടുക്കാനായില്ല. വീണ്ടും ശ്രമിക്കൂ."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"മുഖം പരിശോധിക്കാൻ കഴിയില്ല. ഹാർഡ്വെയർ ലഭ്യമല്ല."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് വീണ്ടും പരീക്ഷിക്കൂ"</string>
<string name="face_error_no_space" msgid="2712120617457553825">"പുതിയ മുഖ ഡാറ്റ സംഭരിക്കാനാകില്ല. ആദ്യം പഴയത് ഇല്ലാതാക്കുക."</string>
<string name="face_error_canceled" msgid="283945501061931023">"മുഖത്തിന്റെ പ്രവർത്തനം റദ്ദാക്കി."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"ഉപയോക്താവ് മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് റദ്ദാക്കി"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"നിരവധി തവണ ശ്രമിച്ചു. പിന്നീട് വീണ്ടും ശ്രമിക്കുക."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"വളരെയധികം ശ്രമങ്ങൾ. മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"മുഖം പരിശോധിക്കാൻ കഴിയില്ല. വീണ്ടും ശ്രമിക്കൂ."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് സജ്ജീകരിച്ചില്ല."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"മുഖം തിരിച്ചറിഞ്ഞുള്ള അൺലോക്ക് ഈ ഉപകരണം പിന്തുണയ്ക്കുന്നില്ല."</string>
<string name="face_name_template" msgid="7004562145809595384">"മുഖം <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1367,8 +1358,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"പരിശോധനാ സംവിധാന മോഡ് പ്രവർത്തനരഹിതമാക്കാൻ ഫാക്ടറി പുനഃക്രമീകരണം നിർവഹിക്കുക."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB പോർട്ടിൽ ദ്രാവകമോ പൊടിയോ കണ്ടെത്തി"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB പോർട്ടർ സ്വയമേവ പ്രവർത്തനരഹിതമായി. കൂടുതലറിയാൻ ടാപ്പ് ചെയ്യുക."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"ഇനി USB പോർട്ട് ഉപയോഗിക്കാം"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"നിലവിൽ ദ്രാവകമോ പൊടിയോ ഉള്ളതായി ഫോൺ കണ്ടെത്തുന്നില്ല."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"ബഗ് റിപ്പോർട്ട് എടുക്കുന്നു…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"ബഗ് റിപ്പോർട്ട് പങ്കിടണോ?"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 13417f2..1c4ae79 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Хурууны хээний дүрс"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"царайгаар тайлах техник хангамжийг удирдах"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Аппад царайны загварыг ашиглахын тулд нэмэх эсвэл устгах аргыг идэвхжүүлэхийг зөвшөөрдөг."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"царайгаар тайлах техник хангамж ашиглах"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Аппад царайгаар тайлах техник хангамжийг баталгаажуулалтад ашиглахыг зөвшөөрдөг"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Царайгаар тайлах"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Царайгаа дахин бүртгүүлнэ үү"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Танилтыг сайжруулахын тулд царайгаа дахин бүртгүүлнэ үү"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Царайн өгөгдлийг зөв авч чадсангүй. Дахин оролдоно уу."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Царайг бататгаж чадсангүй. Техник хангамж боломжгүй байна."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Царайгаар тайлахыг дахин оролдоно уу."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Царайн шинэ өгөгдлийг хадгалж чадсангүй. Эхлээд хуучин өгөгдлийг устгана уу."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Царайны үйл ажиллагааг цуцаллаа."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Хэрэглэгч царайгаар тайлахыг цуцалсан."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Хэт олон удаа оролдлоо. Дараа дахин оролдоно уу."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Хэтэрхий олон удаа оролдлоо. Царайгаар тайлахыг идэвхгүй болголоо."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Царайг бататгаж чадсангүй. Дахин оролдоно уу."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Та царайгаар тайлахыг тохируулаагүй байна."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Царайгаар тайлахыг энэ төхөөрөмж дээр дэмждэггүй."</string>
<string name="face_name_template" msgid="7004562145809595384">"Царай <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Туршилтын цогц горимыг идэвхгүй болгохын тулд үйлдвэрийн төлөвт шинэчилнэ үү."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB порт дээрх шингэн зүйл эсвэл бохирдол"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB портыг автоматаар идэвхгүй болгосон байна. Дэлгэрэнгүй мэдээлэл авахын тулд товшино уу."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB портыг ашиглахад зүгээр"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Утас шингэн зүйл эсвэл бохирдлыг илрүүлсэнгүй."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Алдааны тайланг авч байна..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Алдааны тайланг хуваалцах уу?"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index f4ef749..3ecc71f 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -280,7 +280,7 @@
<string name="permgrouprequest_contacts" msgid="6032805601881764300">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला तुमचे संपर्क अॅक्सेस करू द्यायचे?"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"स्थान"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"या डिव्हाइसच्या स्थानावर प्रवेश"</string>
- <string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला या डिव्हाइसचे स्थान अॅक्सेस करू द्यायचे?"</string>
+ <string name="permgrouprequest_location" msgid="3788275734953323491">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला या डिव्हाइसचे स्थान अॅक्सेस करू द्यायचे?"</string>
<string name="permgrouprequestdetail_location" msgid="1347189607421252902">"तुम्ही अॅप वापरत असताना अॅपला फक्त स्थानाचा अॅक्सेस असेल"</string>
<string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ला <b>प्रत्येक वेळी</b> या डिव्हाइसच्या स्थानाचा अॅक्सेस द्यायचा?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"अॅप सध्या फक्त तुम्ही अॅप वापरत असतानाच स्थान अॅक्सेस करू शकते"</string>
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"फिंगरप्रिंट आयकन"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"फेस अनलॉक हार्डवेअर व्यवस्थापित करा"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"अॅपला वापरासाठी चेहरा टेम्पलेट जोडण्याच्या आणि हटवण्याच्या पद्धती जारी करू देते."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"फेस अनलॉक हार्डवेअर वापरा"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"अॅपला ऑथेंटिकेशनसाठी फेस अनलॉक हार्डवेअर वापरण्याची अनुमती देते"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"फेस अनलॉक"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"तुमच्या चेहऱ्याची पुन्हा नोंदणी करा"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"ओळखण्यामध्ये सुधारणा करण्यासाठी, कृपया तुमच्या चेहऱ्याची पुन्हा नोंदणी करा"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"अचूक फेस डेटा कॅप्चर करता आला नाही. पुन्हा करा."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"चेहरा पडताळू शकत नाही. हार्डवेअर उपलब्ध नाही."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"फेस अनलॉकचा पुन्हा प्रयत्न करा."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"नवीन फेस डेटा स्टोअर करू शकत नाही. आधी जुना हटवा."</string>
<string name="face_error_canceled" msgid="283945501061931023">"चेहरा ऑपरेशन रद्द केले गेले."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"वापरकर्त्याने फेस अनलॉक रद्द केले आहे."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"खूप जास्त प्रयत्न केले. नंतर पुन्हा प्रयत्न करा."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"बरेच प्रयत्न. फेस अनलॉक बंद केले आहे."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"चेहरा पडताळणी करू शकत नाही. पुन्हा प्रयत्न करा."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"तुम्ही फेस अनलॉक सेट केले नाही."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"या डिव्हाइसवर फेस अनलॉकला सपोर्ट नाही."</string>
<string name="face_name_template" msgid="7004562145809595384">"चेहरा <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1367,8 +1358,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"टेस्ट हार्नेस मोड बंद करण्यासाठी फॅक्टरी रीसेट करा."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB पोर्ट मध्ये ओलावा किंवा धूळ आहे"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB पोर्ट आपोआप बंद होईल. अधिक जाणून घेण्यासाठी टॅप करा."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB पोर्ट वापरण्यासाठी ठीक आहे"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"फोनला धूळ किंवा ओलावा आढळला नाही."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"बग रीपोर्ट घेत आहे..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"बग अहवाल शेअर करायचा?"</string>
@@ -1860,7 +1850,7 @@
<string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"आठवड्याची शेवटची रात्र"</string>
<string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"आठवड्याच्या शेवटी"</string>
<string name="zen_mode_default_events_name" msgid="8158334939013085363">"इव्हेंट"</string>
- <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"निष्क्रिय आहे"</string>
+ <string name="zen_mode_default_every_night_name" msgid="3012363838882944175">"झोपताना"</string>
<string name="muted_by" msgid="5942954724562097128">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> काही ध्वनी म्यूट करत आहे"</string>
<string name="system_error_wipe_data" msgid="6608165524785354962">"आपल्या डिव्हाइसमध्ये अंतर्गत समस्या आहे आणि तुमचा फॅक्टरी डेटा रीसेट होईपर्यंत ती अस्थिर असू शकते."</string>
<string name="system_error_manufacturer" msgid="8086872414744210668">"आपल्या डिव्हाइसमध्ये अंतर्गत समस्या आहे. तपशीलांसाठी आपल्या निर्मात्याशी संपर्क साधा."</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 2961f37..0124af4 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikon cap jari"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"urus perkakasan wajah buka kunci"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Membenarkan apl menggunakan kaedah untuk menambahkan dan memadamkan templat wajah untuk digunakan."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"gunakan perkakasan wajah buka kunci"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Membenarkan apl menggunakan perkakasan wajah buka kunci untuk pengesahan"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Wajah buka kunci"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Daftarkan semula wajah anda"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Untuk meningkatkan pengecaman, sila daftarkan semula wajah anda"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Gagal menangkap data wajah dgn tepat. Cuba lagi."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Tdk dpt sahkan wajah. Perkakasan tidak tersedia."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Cuba wajah buka kunci sekali lagi."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Tdk dpt menyimpan data wajah baharu. Padamkan yg lama dahulu."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Pengendalian wajah dibatalkan."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Wajah buka kunci dibatalkan oleh pengguna."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Terlalu banyak percubaan. Cuba sebentar lagi."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Terlalu banyak percubaan. Wajah buka kunci dilumpuhkan."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Tidak dapat mengesahkan wajah. Cuba lagi."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Anda belum menyediakan wajah buka kunci."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Wajah buka kunci tidak disokong pada peranti ini."</string>
<string name="face_name_template" msgid="7004562145809595384">"Wajah <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Laksanakan tetapan semula kilang untuk melumpuhkan Mod Abah-abah Ujian."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Cecair atau serpihan dalam port USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Port USB dilumpuhkan secara automatik. Ketik untuk mengetahui lebih lanjut."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"OK untuk menggunakan port USB"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Telefon tidak lagi mengesan cecair atau serpihan."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Mengambil laporan pepijat…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Kongsi laporan pepijat?"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index f59301a..9090c1d 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1494,7 +1494,7 @@
<string name="find_next" msgid="5742124618942193978">"နောက်တစ်ခု ရှာဖွေရန်"</string>
<string name="find_previous" msgid="2196723669388360506">"အရင်တစ်ခု ရှာဖွေရန်"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"<xliff:g id="NAME">%s</xliff:g>မှ တည်နေရာအား တောင်းခံသည်"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"တည်နေရာအား တောင်းခံသည်"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"တည်နေရာ တောင်းခံခြင်း"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"<xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)မှတောင်းခံသည်"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"Yes"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"No"</string>
@@ -1888,7 +1888,7 @@
<string name="country_selection_title" msgid="2954859441620215513">"ဒေသရွေးချယ်မှု"</string>
<string name="search_language_hint" msgid="7042102592055108574">"ဘာသာစကားအမည် ထည့်ပါ"</string>
<string name="language_picker_section_suggested" msgid="8414489646861640885">"အကြံပြုထားသည်"</string>
- <string name="language_picker_section_all" msgid="3097279199511617537">"ဘာသာစကားများအားလုံး"</string>
+ <string name="language_picker_section_all" msgid="3097279199511617537">"ဘာသာစကားအားလုံး"</string>
<string name="region_picker_section_all" msgid="8966316787153001779">"ဒေသအားလုံး"</string>
<string name="locale_search_menu" msgid="2560710726687249178">"ရှာဖွေရန်"</string>
<string name="app_suspended_title" msgid="2075071241147969611">"အက်ပ်ကို မရရှိနိုင်ပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 11cc95f..c19b1b0 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikon for fingeravtrykk"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"administrere maskinvare for Ansiktslås"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Lar appen bruke metoder for å legge til og slette ansiktmaler for bruk."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"bruke maskinvare for Ansiktslås"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Lar appen bruke maskinvare for Ansiktslås til autentisering"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Ansiktslås"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Registrer ansiktet ditt på nytt"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"For å forbedre gjenkjennelse, registrer ansiktet ditt på nytt"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Kunne ikke ta opp nøyaktige ansiktsdata Prøv igjen"</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Kan ikke bekrefte ansikt. Utilgjengelig maskinvare."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Prøv Ansiktslås igjen."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Kan ikke lagre nye ansiktsdata. Slett gamle data først."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Ansikt-operasjonen ble avbrutt."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Ansiktslås ble avbrutt av brukeren."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"For mange forsøk. Prøv igjen senere."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"For mange forsøk. Ansiktslås er slått av."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Kan ikke bekrefte ansiktet. Prøv igjen."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Du har ikke konfigurert Ansiktslås."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Ansiktslås støttes ikke på denne enheten"</string>
<string name="face_name_template" msgid="7004562145809595384">"Ansikt <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Tilbakestill enheten til fabrikkstandard for å slå av Testrammeverk-modus."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Væske eller rusk i USB-porten"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-porten deaktiveres automatisk. Trykk for å finne ut mer."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Trygt å bruke USB-porten"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Telefonen oppdager ikke væsker eller rusk lenger."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Kjører feilrapport …"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Vil du dele feilrapporten?"</string>
@@ -1907,7 +1897,7 @@
<string name="work_mode_off_message" msgid="5130856710614337649">"Jobbappene dine samt varsler, data og andre funksjoner i jobbprofilen din blir slått på"</string>
<string name="work_mode_turn_on" msgid="2062544985670564875">"Slå på"</string>
<string name="deprecated_target_sdk_message" msgid="1449696506742572767">"Denne appen er utviklet for en eldre versjon av Android og fungerer kanskje ikke som den skal. Prøv å se etter oppdateringer, eller kontakt utvikleren."</string>
- <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Se etter oppdatering"</string>
+ <string name="deprecated_target_sdk_app_store" msgid="5032340500368495077">"Se etter oppdateringer"</string>
<string name="new_sms_notification_title" msgid="8442817549127555977">"Du har nye meldinger"</string>
<string name="new_sms_notification_content" msgid="7002938807812083463">"Åpne SMS-appen for å se"</string>
<string name="profile_encrypted_title" msgid="4260432497586829134">"Enkelte funksjoner kan begrenses"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index db9ca87..0210b6c 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"फिंगरप्रिन्ट आइकन"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"फेस अनलकको हार्डवेयर व्यवस्थित गर्नुहोस्"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"अनुप्रयोगलाई प्रयोगका लागि अनुहार टेम्प्लेट थप्न र मेटाउने तरिका आह्वान गर्न अनुमति दिन्छ।"</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"फेस अनलकको हार्डवेयर प्रयोग गर्नुहोस्"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"अनुप्रयोगलाई प्रमाणीकरणका लागि फेस अनलकको हार्डवेयर प्रयोग गर्न अनुमति दिन्छ"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"फेस अनलक"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"आफ्नो अनुहार पुनः दर्ता गर्नुहोस्"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"अनुहार पहिचानको गुणस्तर सुधार गर्न कृपया आफ्नो अनुहार पुनः दर्ता गर्नुहोस्"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"अनुहारको सटीक डेटा खिच्न सकिएन। फेरि प्रयास गर्नुहोस्।"</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"अनुहार पुष्टि गर्न सकिएन। हार्डवेयर उपलब्ध छैन।"</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"फेरि फेस अनलक प्रयोग गरी हेर्नुहोस्।"</string>
<string name="face_error_no_space" msgid="2712120617457553825">"अनुहारसम्बन्धी नयाँ डेटा भण्डारण गर्न सकिएन। पहिले कुनै पुरानो डेटा मेटाउनुहोस्।"</string>
<string name="face_error_canceled" msgid="283945501061931023">"अनुहार पहिचान रद्द गरियो।"</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"प्रयोगकर्ताले फेस अनलकको कार्य रद्द गर्नुभयो।"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"धेरैपटक प्रयासहरू भए। पछि फेरि प्रयास गर्नुहोस्।"</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"अत्यधिक प्रयासहरू भए। फेस अनलक असक्षम पारियो।"</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"अनुहार पुष्टि गर्न सकिएन। फेरि प्रयास गर्नुहोस्।"</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"तपाईंले फेस अनलक सुविधा सेट अप गर्नुभएको छैन।"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"यस यन्त्रमा फेस अनलक सुविधा प्रयोग गर्न मिल्दैन।"</string>
<string name="face_name_template" msgid="7004562145809595384">"अनुहार <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1372,8 +1363,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"परीक्षण प्याकेज मोड असक्षम पार्न फ्याक्ट्री रिसेट गर्नुहोस्।"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB पोर्टमा तरल पदार्थ वा धुलो भएको कुरा पत्ता लाग्यो"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB पोर्ट स्वतः असक्षम पारियो। थप जान्न ट्याप गर्नुहोस्।"</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB पोर्ट प्रयोग गर्दा हुन्छ"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"फोनले अब उप्रान्त तरल पदार्थ वा धुलो नभएको पत्ता लगायो"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"बग रिपोर्ट लिँदै..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"बग रिपोर्टलाई साझेदारी गर्ने हो?"</string>
@@ -1509,7 +1499,7 @@
<string name="find_next" msgid="5742124618942193978">"अर्को भेटाउनुहोस्"</string>
<string name="find_previous" msgid="2196723669388360506">"अघिल्लो फेला पार्नुहोस्"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"<xliff:g id="NAME">%s</xliff:g> बाट स्थान अनुरोध"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"स्थान अनुरोध"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"स्थानसम्बन्धी अनुरोध"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"<xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>) द्वारा अनुरोध गरिएको"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"हो"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"होइन"</string>
diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml
index 8ba2c60..93975a9 100644
--- a/core/res/res/values-night/themes_device_defaults.xml
+++ b/core/res/res/values-night/themes_device_defaults.xml
@@ -87,5 +87,7 @@
<style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.ResolverCommon">
<item name="windowLightNavigationBar">false</item>
+ <item name="colorBackgroundFloating">@android:color/GM2_grey_800</item>
+ <item name="textColorSecondary">@android:color/resolver_text_color_secondary_dark</item>
</style>
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index b583c73..e77adf4 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Vingerafdruk-pictogram"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"hardware voor ontgrendelen via gezichtsherkenning beheren"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Hiermee kan de app methoden aanroepen om gezichtstemplates toe te voegen en te verwijderen voor gebruik."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"hardware voor ontgrendelen via gezichtsherkenning gebruiken"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Hiermee kan de app hardware voor ontgrendelen via gezichtsherkenning gebruiken voor verificatie"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Ontgrendelen via gezichtsherkenning"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Je gezicht opnieuw registreren"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Registreer je gezicht opnieuw om de herkenning te verbeteren"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Geen accurate gegevens. Probeer het nog eens."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Kan gezicht niet verifiëren. Hardware niet beschikbaar."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Probeer ontgrendelen via gezichtsherkenning opnieuw."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Kan nieuwe gezichten niet opslaan. Verwijder eerst een oude."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Bewerking voor gezichtsherkenning geannuleerd."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Ontgrendelen via gezichtsherkenning geannuleerd door gebruiker."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Te veel pogingen. Probeer het later opnieuw."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Te veel pogingen. Ontgrendelen via gezichtsherkenning uitgeschakeld."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Kan gezicht niet verifiëren. Probeer het nog eens."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Je hebt ontgrendelen via gezichtsherkenning niet ingesteld."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Ontgrendelen via gezichtsherkenning wordt niet ondersteund op dit apparaat."</string>
<string name="face_name_template" msgid="7004562145809595384">"Gezicht <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Reset de fabrieksinstellingen om de test harness-modus uit te schakelen."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Vloeistof of vuil in USB-poort"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-poort is automatisch uitgeschakeld. Tik voor meer informatie."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB-poort kan worden gebruikt"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"De telefoon detecteert geen vloeistof of vuil meer."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Bugrapport genereren…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Bugrapport delen?"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 112f974..2319502 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ଆଇକନ୍"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"ଫେସ୍ ଅନ୍ଲକ୍ ହାର୍ଡୱେର୍ ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"ବ୍ୟବହାର ପାଇଁ ଆପ୍କୁ ଫେସିଆଲ୍ ଟେମ୍ପଲେଟ୍ ଯୋଡିବା ଓ ଡିଲିଟ୍ ର ପଦ୍ଧତି ପାଇଁ ଅନୁମତି ଦିଅନ୍ତୁ।"</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"ଫେସ୍ ଅନ୍ଲକ୍ ହାର୍ଡୱେର୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"ପ୍ରାମାଣିକତା ପାଇଁ ଫେସ୍ ଅନ୍ଲକ୍ ହାର୍ଡୱେର୍ର ବ୍ୟବହାର ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦିଅନ୍ତୁ।"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"ଫେସ୍ ଅନ୍ଲକ୍"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"ଆପଣଙ୍କର ମୁହଁ ପୁଣି-ଏନ୍ରୋଲ୍ କରନ୍ତୁ"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"ଚିହ୍ନଟକରଣକୁ ଉନ୍ନତ କରିବା ପାଇଁ, ଦୟାକରି ଆପଣଙ୍କର ମୁହଁ ପୁଣି-ଏନ୍ରୋଲ୍ କରନ୍ତୁ।"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"ମୁହଁର ଡାଟା କ୍ୟାପଚର୍ ହେଲାନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"ମୁହଁ ଚିହ୍ନଟ କରିପାରିଲା ନାହିଁ। ହାର୍ଡୱେୟାର୍ ଉପଲବ୍ଧ ନାହିଁ।"</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"ଫେସ୍ ଅନ୍ଲକ୍ ପୁଣି ବ୍ୟବହାର କରି ଦେଖନ୍ତୁ"</string>
<string name="face_error_no_space" msgid="2712120617457553825">"ନୂଆ ମୁହଁ ଡାଟା ଷ୍ଟୋର୍ ହେବ ନାହିଁ। ପ୍ରଥମେ ପୁରୁଣାକୁ ଡିଲିଟ୍ କରନ୍ତୁ।"</string>
<string name="face_error_canceled" msgid="283945501061931023">"ଫେସ୍ର ଅପରେଶନ୍ କ୍ୟାନ୍ସଲ୍ ହୋଇଗଲା"</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ଦ୍ୱାରା ଫେସ୍ ଅନଲକ୍ ବାତିଲ୍ କରାଯାଇଛି।"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"ବାରମ୍ବାର ଚେଷ୍ଟା। ପରେ ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"ଅତ୍ୟଧିକ ପ୍ରଚେଷ୍ଟା. ଫେସ୍ ଅନ୍ଲକ୍ ଅକ୍ଷମ କରନ୍ତୁ"</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"ମୁହଁ ଚିହ୍ନଟ କରିପାରିଲା ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"ଆପଣ ଫେସ୍ ଅନ୍ଲକ୍ ସେଟ୍ ଅପ୍ କରିନାହାଁନ୍ତି"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"ଏହି ଡିଭାଇସ୍ରେ ଫେସ୍ ଅନ୍ଲକ୍ ସମର୍ଥିତ ନୁହେଁ।"</string>
<string name="face_name_template" msgid="7004562145809595384">"<xliff:g id="FACEID">%d</xliff:g>ଙ୍କ ଫେସ୍"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"ଟେଷ୍ଟ ହାର୍ନେସ୍ ମୋଡ୍ ଅକ୍ଷମ କରିବାକୁ ଏକ ଫ୍ୟାକ୍ଟରୀ ରିସେଟ୍ କରନ୍ତୁ।"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB ପୋର୍ଟରେ ତରଳ ପଦାର୍ଥ ବା ଧୂଳି"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB ପୋର୍ଟ ସ୍ୱଚାଳିତ ଭାବେ ଅକ୍ଷମ ହୋଇଛି। ଅଧିକ ଜାଣିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ।"</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB ପୋର୍ଟ ବ୍ୟବହାର କରିବା ପାଇଁ ଠିକ୍ ଅଟେ"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"ଫୋନ୍ ଆଉ ତରଳ ପଦାର୍ଥ କିମ୍ବା ଧୂଳିର ଚିହ୍ନଟ କରୁନାହିଁ।"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"ବଗ୍ ରିପୋର୍ଟ ନିଆଯାଉଛି…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"ବଗ୍ ରିପୋର୍ଟ ସେୟାର୍ କରିବେ?"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index fcdc900..7b31b83 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਪ੍ਰਤੀਕ"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"ਚਿਹਰਾ ਅਣਲਾਕ ਹਾਰਡਵੇਅਰ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"ਐਪ ਨੂੰ ਵਰਤਣ ਲਈ ਚਿਹਰਾ ਟੈਮਪਲੇਟ ਸ਼ਾਮਲ ਕਰਨ ਜਾਂ ਮਿਟਾਉਣ ਦੀਆਂ ਵਿਧੀਆਂ ਦੀ ਬੇਨਤੀ ਕਰਨ ਦਿੰਦੀ ਹੈ।"</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"ਚਿਹਰਾ ਅਣਲਾਕ ਹਾਰਡਵੇਅਰ ਵਰਤੋ"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"ਐਪ ਨੂੰ ਪ੍ਰਮਾਣੀਕਰਨ ਲਈ ਚਿਹਰਾ ਅਣਲਾਕ ਹਾਰਡਵੇਅਰ ਵਰਤਣ ਦਿੰਦੀ ਹੈ"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"ਚਿਹਰਾ ਅਣਲਾਕ"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"ਆਪਣਾ ਚਿਹਰਾ ਮੁੜ-ਦਰਜ ਕਰੋ"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"ਪਛਾਣ ਨੂੰ ਬਿਹਤਰ ਬਣਾਉਣ ਲਈ, ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ ਚਿਹਰੇ ਨੂੰ ਮੁੜ-ਦਰਜ ਕਰੋ"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"ਸਟੀਕ ਚਿਹਰਾ ਡਾਟਾ ਕੈਪਚਰ ਨਹੀਂ ਹੋਇਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"ਚਿਹਰੇ ਦੀ ਪੁਸ਼ਟੀ ਨਹੀਂ ਹੋ ਸਕੀ। ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ।"</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"ਚਿਹਰਾ ਅਣਲਾਕ ਦੁਬਾਰਾ ਵਰਤ ਕੇ ਦੇਖੋ।"</string>
<string name="face_error_no_space" msgid="2712120617457553825">"ਨਵਾਂ ਚਿਹਰਾ ਡਾਟਾ ਸਟੋਰ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ। ਪਹਿਲਾਂ ਪੁਰਾਣਾ ਹਟਾਓ।"</string>
<string name="face_error_canceled" msgid="283945501061931023">"ਚਿਹਰਾ ਪਛਾਣਨ ਦੀ ਪ੍ਰਕਿਰਿਆ ਰੱਦ ਕੀਤੀ ਗਈ।"</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"ਵਰਤੋਂਕਾਰ ਨੇ ਚਿਹਰਾ ਅਣਲਾਕ ਰੱਦ ਕੀਤਾ।"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"ਹੱਦੋਂ ਵੱਧ ਕੋਸ਼ਿਸ਼ਾਂ। ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ। ਚਿਹਰਾ ਅਣਲਾਕ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"ਚਿਹਰੇ ਦੀ ਪੁਸ਼ਟੀ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"ਤੁਸੀਂ ਚਿਹਰਾ ਅਣਲਾਕ ਸੈੱਟਅੱਪ ਨਹੀਂ ਕੀਤਾ ਹੈ।"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"ਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਚਿਹਰਾ ਅਣਲਾਕ ਦੀ ਸੁਵਿਧਾ ਨਹੀਂ ਹੈ।"</string>
<string name="face_name_template" msgid="7004562145809595384">"ਚਿਹਰਾ <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1367,8 +1358,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"ਟੈਸਟ ਹਾਰਨੈੱਸ ਮੋਡ ਬੰਦ ਕਰਨ ਲਈ ਫੈਕਟਰੀ ਰੀਸੈੱਟ ਕਰੋ।"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB ਪੋਰਟ ਵਿੱਚ ਪਾਣੀ ਜਾਂ ਧੂੜ-ਮਿੱਟੀ"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB ਪੋਰਟ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਬੰਦ ਕੀਤਾ ਗਿਆ। ਹੋਰ ਜਾਣਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB ਪੋਰਟ ਵਰਤਣ ਲਈ ਠੀਕ ਹੈ"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"ਫ਼ੋਨ ਹੁਣ ਪਾਣੀ ਜਾਂ ਧੂੜ-ਮਿੱਟੀ ਦਾ ਪਤਾ ਨਹੀਂ ਲਗਾਉਂਦਾ ਹੈ।"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"ਬੱਗ ਰਿਪਰੋਟ ਪ੍ਰਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"ਕੀ ਬੱਗ ਰਿਪੋਰਟ ਸਾਂਝੀ ਕਰਨੀ ਹੈ?"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 120d256..2dedf9d 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -555,15 +555,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona odcisku palca"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"zarządzanie sprzętem do rozpoznawania twarzy"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Zezwala na aktywowanie przez aplikację metody dodawania i usuwania szablonów twarzy."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"używanie sprzętu do rozpoznawania twarzy"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Zezwala na używanie przez aplikację sprzętu do rozpoznawania twarzy w uwierzytelniania"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Rozpoznanie twarzy"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Zarejestruj swoją twarz ponownie"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Aby poprawić rozpoznawanie, ponownie zarejestruj swoją twarz"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Nie udało się zarejestrować danych twarzy. Spróbuj ponownie."</string>
@@ -589,20 +585,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Nie można zweryfikować twarzy. Sprzęt niedostępny."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Spróbuj rozpoznania twarzy ponownie."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Nie można przechowywać nowych danych twarzy. Usuń stare."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Analiza twarzy została anulowana."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Użytkownik anulował rozpoznanie twarzy."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Zbyt wiele prób. Spróbuj ponownie później."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Zbyt wiele prób. Rozpoznanie twarzy zostało wyłączone."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Nie można zweryfikować twarzy. Spróbuj ponownie."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Rozpoznanie twarzy nie jest skonfigurowane."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"To urządzenie nie obsługuje rozpoznania twarzy."</string>
<string name="face_name_template" msgid="7004562145809595384">"Twarz <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1410,8 +1401,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Przywróć ustawienia fabryczne, by wyłączyć tryb jarzma testowego."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Wilgoć lub brud w porcie USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Port USB został automatycznie wyłączony. Kliknij, by dowiedzieć się więcej."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Możesz używać portu USB"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Telefon nie wykrywa już wilgoci ani zanieczyszczeń."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Zgłaszam błąd…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Udostępnić raport o błędzie?"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 4dedb69..d873d20 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ícone de impressão digital"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"gerenciar hardware de desbloqueio facial"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Permite que o app execute métodos para adicionar e excluir modelos de rosto para uso."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"usar hardware de desbloqueio facial"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Permite que o app use o hardware de desbloqueio facial para autenticação"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Desbloqueio facial"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Registre seu rosto novamente"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Para melhorar o reconhecimento, registre seu rosto novamente"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Dados precisos não capturados. Tente novamente."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Impossível verificar rosto. Hardware indisponível."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Tente usar o desbloqueio facial novamente."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Não é possível salvar dados faciais. Exclua dados antigos."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Operação facial cancelada."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Desbloqueio facial cancelado pelo usuário."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Excesso de tentativas. Tente novamente mais tarde."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Muitas tentativas. Desbloqueio facial desativado."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Não é possível verificar o rosto. Tente novamente."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"O desbloqueio facial não foi configurado."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"O desbloqueio facial não é compatível com este dispositivo."</string>
<string name="face_name_template" msgid="7004562145809595384">"Rosto <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Realize uma redefinição para configuração original para desativar o modo Arcabouço de testes."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Líquido ou detrito na porta USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"A porta USB é desativada automaticamente. Toque para saber mais."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"É seguro usar a porta USB"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Não há mais líquidos ou detritos detectados no smartphone."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Gerando relatório do bug..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Compartilhar relatório do bug?"</string>
@@ -1503,7 +1493,7 @@
<string name="find_next" msgid="5742124618942193978">"Localizar próximo"</string>
<string name="find_previous" msgid="2196723669388360506">"Localizar anterior"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"Solicitação de local de <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"Solicitação de local"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"Pedido de localização"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"Solicitado por <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"Sim"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"Não"</string>
@@ -1598,7 +1588,7 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefone"</string>
- <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Alto-falantes da dock"</string>
+ <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Alto-falantes na base"</string>
<string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
<string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Fones de ouvido"</string>
<string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 7283db2..c2c5d83 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ícone de impressão digital"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"gerir hardware de Desbloqueio Através do Rosto"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Permite à aplicação invocar métodos para adicionar e eliminar modelos faciais para uso."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"utilizar hardware de Desbloqueio Através do Rosto"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Permite que a aplicação utilize hardware de Desbloqueio Através do Rosto para autenticação"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Desbloqueio Através do Rosto"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Volte a inscrever o seu rosto"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Para melhorar o reconhecimento, volte a inscrever o seu rosto."</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Imp. capt. dados rosto precisos. Tente novamente."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Não pode validar o rosto. Hardware não disponível."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Experimente de novo o Desbloqueio Através do Rosto"</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Não pode guardar novos dados de rostos. Elimine um antigo."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Operação de rosto cancelada."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Desbloqueio Através do Rosto cancelado pelo utilizador"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Demasiadas tentativas. Tente novamente mais tarde."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Demasiadas tentativas. O Desbloqueio Através do Rosto está desativado."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Não é possível validar o rosto. Tente novamente."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Não configurou o Desbloqueio Através do Rosto."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Desbloqueio Através do Rosto não suportado neste dispositivo."</string>
<string name="face_name_template" msgid="7004562145809595384">"Rosto <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Efetue uma reposição de dados de fábrica para desativar o Modo de estrutura de teste."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Líquido ou resíduos na porta USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"A porta USB é automaticamente desativada. Toque para saber mais."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"É seguro utilizar a porta USB"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"O telemóvel já não deteta líquidos nem resíduos."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"A criar relatório de erro…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Pretende partilhar o relatório de erro?"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 4dedb69..d873d20 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ícone de impressão digital"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"gerenciar hardware de desbloqueio facial"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Permite que o app execute métodos para adicionar e excluir modelos de rosto para uso."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"usar hardware de desbloqueio facial"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Permite que o app use o hardware de desbloqueio facial para autenticação"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Desbloqueio facial"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Registre seu rosto novamente"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Para melhorar o reconhecimento, registre seu rosto novamente"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Dados precisos não capturados. Tente novamente."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Impossível verificar rosto. Hardware indisponível."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Tente usar o desbloqueio facial novamente."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Não é possível salvar dados faciais. Exclua dados antigos."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Operação facial cancelada."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Desbloqueio facial cancelado pelo usuário."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Excesso de tentativas. Tente novamente mais tarde."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Muitas tentativas. Desbloqueio facial desativado."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Não é possível verificar o rosto. Tente novamente."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"O desbloqueio facial não foi configurado."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"O desbloqueio facial não é compatível com este dispositivo."</string>
<string name="face_name_template" msgid="7004562145809595384">"Rosto <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Realize uma redefinição para configuração original para desativar o modo Arcabouço de testes."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Líquido ou detrito na porta USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"A porta USB é desativada automaticamente. Toque para saber mais."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"É seguro usar a porta USB"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Não há mais líquidos ou detritos detectados no smartphone."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Gerando relatório do bug..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Compartilhar relatório do bug?"</string>
@@ -1503,7 +1493,7 @@
<string name="find_next" msgid="5742124618942193978">"Localizar próximo"</string>
<string name="find_previous" msgid="2196723669388360506">"Localizar anterior"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"Solicitação de local de <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"Solicitação de local"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"Pedido de localização"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"Solicitado por <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"Sim"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"Não"</string>
@@ -1598,7 +1588,7 @@
<string name="default_audio_route_name" product="tablet" msgid="4617053898167127471">"Tablet"</string>
<string name="default_audio_route_name" product="tv" msgid="9158088547603019321">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="4239291273420140123">"Telefone"</string>
- <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Alto-falantes da dock"</string>
+ <string name="default_audio_route_name_dock_speakers" msgid="6240602982276591864">"Alto-falantes na base"</string>
<string name="default_audio_route_name_hdmi" msgid="1486254205617081251">"HDMI"</string>
<string name="default_audio_route_name_headphones" msgid="8119971843803439110">"Fones de ouvido"</string>
<string name="default_audio_route_name_usb" msgid="1234984851352637769">"USB"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 3c2e03e..0d5cff4 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -552,15 +552,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Pictograma amprentă"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"să gestioneze hardware de deblocare facială"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Permite aplicației să invoce metode pentru a adăuga și a șterge șabloane faciale pentru utilizare."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"să folosească hardware de deblocare facială"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Permite aplicației să folosească hardware de deblocare facială pentru autentificare"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Deblocare facială"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Reînregistrați-vă chipul"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Pentru a îmbunătăți recunoașterea, reînregistrați-vă chipul"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Nu s-a putut fotografia fața cu precizie. Încercați din nou."</string>
@@ -586,20 +582,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Nu se poate confirma fața. Hardware-ul nu este disponibil."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Încercați din nou deblocarea facială."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Nu se pot stoca date faciale noi. Ștergeți întâi unele vechi."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Operațiunea privind chipul a fost anulată."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Deblocarea facială este anulată de utilizator."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Prea multe încercări. Reîncercați mai târziu."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Prea multe încercări. Deblocarea facială este dezactivată."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Nu se poate confirma fața. Încercați din nou."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Nu ați configurat deblocarea facială."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Deblocarea facială nu este acceptată pe dispozitiv."</string>
<string name="face_name_template" msgid="7004562145809595384">"Chip <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1388,8 +1379,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Reveniți la setările din fabrică pentru a dezactiva modul Set de testare."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Lichide sau reziduuri în portul USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Portul USB este dezactivat automat. Atingeți ca să aflați mai multe."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Portul USB poate fi folosit"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Telefonul nu mai detectează lichide sau reziduuri."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Se creează un raport de eroare…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Trimiteți raportul de eroare?"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 936f463..2c17d59 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -555,15 +555,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Значок отпечатка пальца"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"Управление аппаратным обеспечением для функции \"Фейсконтроль\""</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Приложение сможет добавлять и удалять шаблоны лиц."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"Использование аппаратного обеспечения для функции \"Фейсконтроль\""</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Приложение сможет использовать для аутентификации аппаратное обеспечение Фейсконтроля."</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Фейсконтроль"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Зарегистрируйте лицо ещё раз"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Чтобы улучшить распознавание лица, зарегистрируйте его ещё раз"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Не удалось собрать данные. Повторите попытку."</string>
@@ -589,20 +585,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Не удалось распознать лицо. Сканер недоступен."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Попробуйте воспользоваться функцией ещё раз."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Недостаточно места. Удалите старые данные для распознавания."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Распознавание отменено"</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Фейсконтроль: операция отменена пользователем."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Слишком много попыток. Повторите позже."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Слишком много попыток. Функция \"Фейсконтроль\" отключена."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Не удалось распознать лицо. Повторите попытку."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Вы не настроили функцию \"Фейсконтроль\"."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Это устройство не поддерживает функцию \"Фейсконтроль\"."</string>
<string name="face_name_template" msgid="7004562145809595384">"Лицо <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1410,8 +1401,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Чтобы отключить тестовый режим, сбросьте настройки до заводских."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"В USB-порт попала вода или грязь"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-порт был автоматически отключен. Нажмите, чтобы узнать подробности."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB-порт можно использовать"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Вода или грязь внутри не обнаружена."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Подготовка отчета об ошибке"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Разрешить доступ к информации об ошибке?"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 3a8136d..cb12d41 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -555,15 +555,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona odtlačku prsta"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"spravovať hardvér odomknutia tvárou"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Umožňuje aplikácii vyvolať metódy, ktoré pridávajú a odstraňujú šablóny tvárí."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"používať hardvér Odomknutia tvárou"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Umožňuje aplikácii používať na overenie totožnosti hardvér Odomknutia tvárou"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Odomknutie tvárou"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Znova zaregistrujte svoju tvár"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Znova zaregistrujte svoju tvár, aby sa zlepšilo rozpoznávanie"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Nepodarilo sa nasnímať presné údaje o tvári. Skúste to znova."</string>
@@ -589,20 +585,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Tvár sa nedá overiť. Hardvér nie je k dispozícii."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Skúste znova použiť odomknutie tvárou."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Nové údaje o tvári sa nedajú uložiť. Najprv odstráňte jeden zo starých záznamov."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Operácia týkajúca sa tváre bola zrušená"</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Odomknutie tvárou zrušil používateľ."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Príliš veľa pokusov. Skúste to znova neskôr."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Príliš veľa pokusov. Odomknutie tvárou bolo zakázané."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Nedá sa overiť tvár. Skúste to znova."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Nenastavili ste odomknutie tvárou."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Toto zariadenie nepodporuje odomknutie tvárou."</string>
<string name="face_name_template" msgid="7004562145809595384">"Tvár <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1410,8 +1401,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Ak chcete zakázať režim správcu testov, obnovte výrobné nastavenia."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Tekutina alebo nečistoty v porte USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Port USB je automaticky deaktivovaný. Ďalšie informácie zobrazíte klepnutím."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Port USB môžete použiť"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Telefón už nerozpoznáva tekutinu ani nečistoty."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Preberá sa hlásenie chyby…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Chcete zdieľať hlásenie chyby?"</string>
@@ -1549,7 +1539,7 @@
<string name="find_next" msgid="5742124618942193978">"Nájsť ďalšiu"</string>
<string name="find_previous" msgid="2196723669388360506">"Nájsť predchádzajúcu"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"Žiadosť o informácie o polohe od používateľa <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"Žiadosť o informácie o polohe"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"Žiadosť o informácie o polohe"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"Žiadosť od používateľa <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"Áno"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"Nie"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 84e63cc..088eb48 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -555,15 +555,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona prstnih odtisov"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"upravljanje strojne opreme za odklepanje z obrazom"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Aplikaciji omogoča sprožanje načinov za dodajanje in brisanje predlog z obrazi za uporabo."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"uporaba strojne opreme za odklepanje z obrazom"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Aplikaciji omogoča uporabo strojne opreme za odklepanje z obrazom za preverj. pristnosti"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Odklepanje z obrazom"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Znova prijavite obraz"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Za izboljšanje prepoznavanja znova prijavite svoj obraz"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Točnih podatkov o obrazu ni bilo mogoče zajeti. Poskusite znova."</string>
@@ -589,20 +585,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Obraza ni mogoče preveriti. Str. opr. ni na voljo."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Znova izvedite odklepanje z obrazom."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Novega obraza ni mogoče shraniti. Najprej izbrišite starega."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Dejanje z obrazom je bilo preklicano."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Odklepanje z obrazom je preklical uporabnik."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Preveč poskusov. Poskusite znova pozneje."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Preveč poskusov. Odklepanje z obrazom je onemogočeno."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Obraza ni mogoče preveriti. Poskusite znova."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Odklepanja z obrazom niste nastavili."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Ta naprava ne podpira odklepanja z obrazom."</string>
<string name="face_name_template" msgid="7004562145809595384">"Obraz <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1410,8 +1401,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Če želite onemogočiti način preizkusnega ogrodja, ponastavite napravo na tovarniške nastavitve."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"V vratih USB je tekočina ali umazanija"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Vrata USB so samodejno onemogočena. Dotaknite se, če želite izvedeti več."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Vrata USB so varna za uporabo"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Telefon ne zaznava več tekočine ali umazanije."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Zajemanje poročila o napakah …"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Želite poslati poročilo o napakah?"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 202d090..4c2543c7 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikona e gjurmës së gishtit"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"menaxho harduerin për shkyçjen me fytyrën"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Lejon aplikacionin të aktivizojë mënyra për shtim e fshirje të shablloneve të përdorura."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"përdor harduerin e shkyçjes me fytyrën"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Lejon aplikacionin të përdorë harduerin e shkyçjes me fytyrën për procesin e vërtetimit"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Shkyçja me fytyrë"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Regjistro përsëri fytyrën tënde"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Për të përmirësuar njohjen, regjistro përsëri fytyrën tënde"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"S\'mund të regjistroheshin të dhëna të sakta të fytyrës. Provo përsëri."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Fytyra s\'mund të verifikohet. Hardueri nuk ofrohet."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Provo përsëri shkyçjen me fytyrën."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"S\'mund të ruhen të dhëna të reja fytyre. Fshi një të vjetër në fillim."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Veprimi me fytyrën u anulua."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Shkyçja me fytyrë u anulua nga përdoruesi."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Shumë përpjekje. Provo sërish më vonë."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Shumë përpjekje. Shkyçja me fytyrë u çaktivizua."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Fytyra nuk mund të verifikohet. Provo përsëri."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Nuk e ke konfiguruar shkyçjen me fytyrë."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Shkyçja me fytyrë nuk mbështetet në këtë pajisje"</string>
<string name="face_name_template" msgid="7004562145809595384">"Fytyra <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1367,8 +1358,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Kryej një rivendosje në cilësimet e fabrikës për të çaktivizuar \"Modalitetin e lidhjes së testimit\"."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Lëngje ose papastërti në portën e USB-së"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Porta e USB-së është çaktivizuar automatikisht. Trokit për të mësuar më shumë."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Në rregulloj për përdorimin e portës USB"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Telefoni nuk i dallon më lëngjet apo papastërtitë."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Po merret raporti i defekteve në kod…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Të ndahet raporti i defektit në kod?"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 812af02..e95d154 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -552,15 +552,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Икона отиска прста"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"управљање хардв. за откључавање лицем"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Дозвољава да апликација активира методе за додавање и брисање шаблона лица ради коришћења."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"коришћење хардвера за откључавање лицем"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Дозвољава да апликација користи хардвер за откључавање лицем ради потврде идентитета"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Откључавање лицем"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Поново региструјте лице"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Да бисте побољшали препознавање, поново региструјте лице"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Снимање лица није успело. Пробајте поново."</string>
@@ -586,20 +582,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Провера лица није успела. Хардвер није доступан."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Пробајте поново откључавање лицем."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Нови подаци о лицу нису сачувани. Прво избришете претходне."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Обрада лица је отказана."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Корисник је отказао откључавање лицем"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Превише покушаја. Пробајте поново касније."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Превише покушаја. Откључавање лицем је онемогућено."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Провера лица није успела. Пробајте поново."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Нисте подесили откључавање лицем"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Откључавање лицем није подржано на овом уређају"</string>
<string name="face_name_template" msgid="7004562145809595384">"Лице <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1388,8 +1379,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Обавите ресетовање на фабричка подешавања да бисте онемогућили режим пробног коришћења."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Течност или нечистоћа у USB порту"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB порт је аутоматски искључен. Додирните да бисте сазнали више."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Коришћење USB порта је дозвољено"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Телефон више не открива течност или нечистоћу."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Извештај о грешци се генерише…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Желите ли да поделите извештај о грешци?"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index b3e95f9..6335fcd 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Ikon för fingeravtryck"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"hantera maskinvara för ansiktslås"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Tillåter att appen anropar metoder för att lägga till och radera ansiktsmallar."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"använd maskinvara för ansiktslås"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Tillåter att appen använder maskinvara för ansiktslås vid autentisering"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Ansiktslås"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Registrera ansiktet på nytt"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Gör om registreringen av ansiktet så att ansiktsigenkänningen ska fungera bättre"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Det gick inte att fånga ansiktsdata. Försök igen."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Ansiktsverifiering går ej. Otillgänglig maskinvara."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Försök att använda ansiktslåset igen."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Kan inte lagra ny ansiktsdata. Radera först gammal data."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Ansiktsåtgärden har avbrutits."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Ansiktslås avbröts av användaren."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Du har gjort för många försök. Försök igen senare."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"För många försök. Ansiktslås har inaktiverats."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Det gick inte att verifiera ansiktet. Försök igen."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Du har inte konfigurerat ansiktslås."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Ansiktslås stöds inte på den här enheten."</string>
<string name="face_name_template" msgid="7004562145809595384">"Ansikte <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Inaktivera testverktygsläget genom att göra en återställning till standardinställningarna."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Vätska eller smuts i USB-porten"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-porten har inaktiverats automatiskt. Tryck för att läsa mer."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Nu kan du använda USB-porten"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Mobilen känner inte längre av någon vätska eller smuts."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Felrapporten överförs …"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Vill du dela felrapporten?"</string>
@@ -1503,7 +1493,7 @@
<string name="find_next" msgid="5742124618942193978">"Sök nästa"</string>
<string name="find_previous" msgid="2196723669388360506">"Sök föregående"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"Positionsförfrågan från <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"Positionsförfrågan"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"Platsförfrågan"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"Begärt av <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"Ja"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"Nej"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index b18543e..4d5821a 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Aikoni ya kitambulisho"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"dhibiti maunzi ya kufungua kwa uso"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Huruhusu programu iombe njia za kuongeza na kufuta violezo vya uso vitakavyotumiwa."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"tumia maunzi ya kufungua kwa uso"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Huruhusu programu itumie maunzi ya kufungua kwa uso kwa ajili ya uthibitishaji"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Kufungua kwa uso"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Sajili uso wako tena"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Ili kuimarisha utambuzi, tafadhali sajili uso wako tena"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Imeshindwa kunasa data sahihi ya uso. Jaribu tena."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Imeshindwa kuthibitisha uso. Maunzi hayapatikani."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Jaribu kufungua kwa uso tena."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Imeshindwa kuhifadhi data ya uso mpya. Futa wa kale kwanza."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Utendaji wa kitambulisho umeghairiwa."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Kufungua kwa uso kumeghairiwa na mtumiaji."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Umejaribu mara nyingi mno. Jaribu tena baadaye."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Umejaribu mara nyingi mno. Umezima kipengele cha kufungua kwa uso."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Imeshindwa kuthibitisha uso. Jaribu tena."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Hujaweka mipangilio ya kufungua kwa uso."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Kufungua kwa uso hakutumiki kwenye kifaa hiki."</string>
<string name="face_name_template" msgid="7004562145809595384">"Uso wa <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Rejesha mipangilio iliyotoka nayo kiwandani ili uzime hali ya Muunganisho wa Majaribio."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Unyevu au uchafu katika mlango wa USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Mlango wa USB umezimwa kiotomatiki. Gusa ili upate maelezo zaidi."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Ni sawa kutumia mlango wa USB"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Simu haitambui tena unyevu au uchafu."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Inatayarisha ripoti ya hitilafu…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Ungependa kushiriki ripoti ya hitilafu?"</string>
@@ -1503,7 +1493,7 @@
<string name="find_next" msgid="5742124618942193978">"Pata ifuatayo"</string>
<string name="find_previous" msgid="2196723669388360506">"Pata iliyotangulia"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"Ombi la mahali kutoka <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"Ombi la mahali"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"Ombi la kutambua mahali"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"Imeombwa na <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"Ndiyo"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"Hapana"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 3215fec..b87a112 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"கைரேகை ஐகான்"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"\'முகம் காட்டித் திறத்தலுக்கான\' வன்பொருளை நிர்வகித்தல்"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"உபயோகிப்பதற்காக முக டெம்ப்ளேட்டுகளை சேர்க்கும்/நீக்கும் முறைகளை இயக்க, ஆப்ஸை அனுமதிக்கும்."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"’முகம் காட்டித் திறத்தல்’ அம்சத்திற்கான வன்பொருளைப் பயன்படுத்துதல்"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"அடையாளம் காண \'முகம் காட்டித் திறத்தலுக்கான\' வன்பொருளைப் பயன்படுத்த ஆப்ஸை அனுமதிக்கும்"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"முகம் காட்டித் திறத்தல்"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"முகத்தை மீண்டும் பதிவுசெய்யவும்"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"அடையாளத்தை மேம்படுத்த முகத்தை மீண்டும் பதிவுசெய்யவும்"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"முகம் தெளிவாகப் பதிவாகவில்லை. மீண்டும் முயலவும்."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"முகத்தைச் சரிபார்க்க இயலவில்லை. வன்பொருள் இல்லை."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"\'முகம் காட்டித் திறத்தலை\' மீண்டும் முயலவும்."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"புதிய முகங்களைச் சேர்க்க இயலவில்லை. பழையது ஒன்றை நீக்கவும்."</string>
<string name="face_error_canceled" msgid="283945501061931023">"முக அங்கீகாரச் செயல்பாடு ரத்துசெய்யப்பட்டது."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"\'முகம் காட்டித் திறத்தல்\' பயனரால் ரத்துசெய்யப்பட்டது."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"பலமுறை முயன்றுவிட்டீர்கள். பிறகு முயலவும்."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"பலமுறை முயன்றுவிட்டீர்கள். \'முகம் காட்டித் திறத்தல்’ முடக்கப்பட்டது."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"முகத்தைச் சரிபார்க்க இயலவில்லை. மீண்டும் முயலவும்."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"’முகம் காட்டித் திறத்தலை’ நீங்கள் அமைக்கவில்லை."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"இந்த சாதனத்தில் ’முகம் காட்டித் திறத்தல்’ ஆதரிக்கப்படவில்லை."</string>
<string name="face_name_template" msgid="7004562145809595384">"முகம் <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1367,8 +1358,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"’தன்னியக்க சோதனைப்\' பயன்முறையை முடக்க ஆரம்பநிலைக்கு மீட்டமைக்கவும்."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB போர்ட்டில் சேதம் உள்ளது"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB போர்ட் தானாகவே முடக்கப்பட்டது மேலும் அறிய, தட்டவும்."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB போர்ட்டைப் பயன்படுத்தலாம்"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"மொபைலில் எந்தச் சேதாரமும் கண்டறியப்படவில்லை."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"பிழை அறிக்கையை எடுக்கிறது…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"பிழை அறிக்கையைப் பகிரவா?"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 908f10d..ff1643c 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -280,7 +280,7 @@
<string name="permgrouprequest_contacts" msgid="6032805601881764300">"మీ పరిచయాలను యాక్సెస్ చేయడానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ని అనుమతించాలా?"</string>
<string name="permgrouplab_location" msgid="7275582855722310164">"స్థానం"</string>
<string name="permgroupdesc_location" msgid="1346617465127855033">"ఈ పరికర స్థానాన్ని యాక్సెస్ చేయడానికి"</string>
- <string name="permgrouprequest_location" msgid="3788275734953323491">"ఈ పరికర స్థానాన్ని యాక్సెస్ చేయడానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ని అనుమతించాలా?"</string>
+ <string name="permgrouprequest_location" msgid="3788275734953323491">"ఈ పరికర స్థానాన్ని యాక్సెస్ చేయడానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ను అనుమతించాలా?"</string>
<string name="permgrouprequestdetail_location" msgid="1347189607421252902">"మీరు యాప్ను ఉపయోగిస్తున్నప్పుడు మాత్రమే స్థానానికి యాప్ యాక్సెస్ కలిగి ఉంటుంది"</string>
<string name="permgroupbackgroundrequest_location" msgid="5039063878675613235">"ఈ పరికర స్థానాన్ని <b>అన్ని సమయాలలో</b> యాక్సెస్ చేయడానికి <b><xliff:g id="APP_NAME">%1$s</xliff:g></b>ను అనుమతించాలా?"</string>
<string name="permgroupbackgroundrequestdetail_location" msgid="4597006851453417387">"యాప్ ప్రస్తుతం మీరు ఆ యాప్ను ఉపయోగిస్తున్నప్పుడు మాత్రమే స్థానాన్ని యాక్సెస్ చేయగలుగుతుంది"</string>
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"వేలిముద్ర చిహ్నం"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"ముఖంతో అన్లాక్ చేయగల హార్డ్వేర్ నిర్వహణ"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"వినియోగం కోసం ముఖ టెంప్లేట్లను జోడించే మరియు తొలగించే పద్ధతులను అమలు చేయడానికి యాప్ను అనుమతిస్తుంది."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"ముఖంతో అన్లాక్ చేయగల హార్డ్వేర్ వినియోగం"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"ప్రమాణీకరణ కోసం ముఖంతో అన్లాక్ చేయగల హార్డ్వేర్ను ఉపయోగించడానికి యాప్ను అనుమతిస్తుంది"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"ముఖంతో అన్లాక్"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"మీ ముఖాన్ని తిరిగి నమోదు చేయండి"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"గుర్తింపును మెరుగుపరచడానికి, దయచేసి మీ ముఖంను తిరిగి నమోదు చేసుకోండి"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"ముఖం డేటా సరిగ్గా రాలేదు. మళ్లీ ప్రయత్నించండి."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"ముఖం ధృవీకరించలేరు. హార్డ్వేర్ అందుబాటులో లేదు."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"ముఖంతో అన్లాక్ను మళ్లీ ప్రయత్నించండి."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"కొత్త ముఖం డేటాను నిల్వ చేయడం కాదు. మొదట పాతది తొలిగించండి."</string>
<string name="face_error_canceled" msgid="283945501061931023">"ముఖ కార్యకలాపం రద్దయింది."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"ముఖంతో అన్లాక్ను వినియోగదారు రద్దు చేశారు."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"చాలా ఎక్కువ ప్రయత్నాలు చేసారు. తర్వాత మళ్లీ ప్రయత్నించండి."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"అనేకసార్లు ప్రయత్నించారు. ముఖంతో అన్లాక్ నిలిపివేయబడింది."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"ముఖం ధృవీకరించలేకపోయింది. మళ్లీ ప్రయత్నించండి."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"మీరు ముఖంతో అన్లాక్ను సెటప్ చేయలేదు."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"ఈ పరికరంలో ముఖంతో అన్లాక్ను ఉపయోగించడానికి మద్దతు లేదు."</string>
<string name="face_name_template" msgid="7004562145809595384">"ముఖ <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1367,8 +1358,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"పరీక్ష నియంత్రణ మోడ్ను నిలిపివేయడానికి ఫ్యాక్టరీ రీసెట్ను అమలు చేయండి."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB పోర్ట్లో ద్రవ లేదా వ్యర్థ పదార్థాలు ఉన్నాయి"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB పోర్ట్ ఆటోమేటిక్గా నిలిపివేయబడింది. మరింత తెలుసుకోవడానికి నొక్కండి."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB పోర్ట్ను ఉపయోగించడం సురక్షితం"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"ఫోన్ ఇకపై ద్రవ లేదా వ్యర్థ పదార్థాలను గుర్తించదు."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"బగ్ నివేదికను తీస్తోంది…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"బగ్ నివేదికను భాగస్వామ్యం చేయాలా?"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index fb00cee..42294da 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"ไอคอนลายนิ้วมือ"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"จัดการฮาร์ดแวร์ Face Unlock"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"อนุญาตให้แอปเรียกใช้วิธีเพิ่มและลบเทมเพลตใบหน้าสำหรับการใช้งาน"</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"ใช้ฮาร์ดแวร์ Face Unlock"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"อนุญาตให้แอปใช้ฮาร์ดแวร์ Face Unlock เพื่อตรวจสอบสิทธิ์"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Face Unlock"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"ลงทะเบียนใบหน้าอีกครั้ง"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"โปรดลงทะเบียนใบหน้าอีกครั้งเพื่อปรับปรุงการจดจำ"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"บันทึกข้อมูลใบหน้าที่ถูกต้องไม่ได้ ลองอีกครั้ง"</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"ยืนยันใบหน้าไม่ได้ ฮาร์ดแวร์ไม่พร้อมใช้งาน"</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"ลองใช้ Face Unlock อีกครั้ง"</string>
<string name="face_error_no_space" msgid="2712120617457553825">"จัดเก็บข้อมูลใบหน้าใหม่ไม่ได้ ลบข้อมูลเก่าออกไปก่อน"</string>
<string name="face_error_canceled" msgid="283945501061931023">"ยกเลิกการดำเนินการกับใบหน้าแล้ว"</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"ผู้ใช้ยกเลิกการใช้ Face Unlock"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"ดำเนินการหลายครั้งเกินไป ลองอีกครั้งในภายหลัง"</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"ลองหลายครั้งเกินไป ปิดใช้ Face Unlock แล้ว"</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"ยืนยันใบหน้าไม่ได้ ลองอีกครั้ง"</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"คุณยังไม่ได้ตั้งค่า Face Unlock"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"อุปกรณ์นี้ไม่รองรับ Face Unlock"</string>
<string name="face_name_template" msgid="7004562145809595384">"ใบหน้า <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"รีเซ็ตเป็นค่าเริ่มต้นเพื่อปิดใช้โหมดโปรแกรมทดสอบอัตโนมัติ"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"มีของเหลวหรือฝุ่นละอองในพอร์ต USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"พอร์ต USB ปิดใช้โดยอัตโนมัติ แตะเพื่อดูข้อมูลเพิ่มเติม"</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"ใช้พอร์ต USB ได้แล้ว"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"โทรศัพท์ไม่ได้ตรวจจับของเหลวและฝุ่นละอองแล้ว"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"กำลังสร้างรายงานข้อบกพร่อง…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"แชร์รายงานข้อบกพร่องไหม"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 73439bb..346a0b6 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Icon ng fingerprint"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"pamahalaan ang hardware ng face unlock"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Pumapayag na mag-invoke ang app ng paraang magdagdag at mag-delete ng template ng mukha."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"gamitin ang hardware ng face unlock"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Pinapayagan ang app na gamitin ang hardware ng face unlock para sa pag-authenticate"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Face unlock"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"I-enroll ulit ang iyong mukha"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Para mapahusay ang pagkilala, paki-enroll ulit ang iyong mukha"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Hindi makakuha ng tamang face data. Subukang muli."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Di ma-verify ang mukha. Di available ang hardware."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Subukan ulit ang face unlock."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Hindi ma-store ang data ng mukha. Mag-delete muna ng iba."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Nakansela ang operation kaugnay ng mukha."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Kinansela ng user ang face unlock."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Masyadong maraming pagsubok. Subukang muli mamaya."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Masyadong maraming pagsubok. Na-disable ang face unlock."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Hindi ma-verify ang mukha. Subukang muli."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Hindi mo pa nase-set up ang face unlock."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Hindi sinusuportahan ang face unlock sa device na ito."</string>
<string name="face_name_template" msgid="7004562145809595384">"Mukha <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Mag-factory reset para i-disable ang Test Harness Mode."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Liquid o debris sa USB port"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Awtomatikong na-disable ang USB port. Mag-tap para matuto pa."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Ayos na gamitin ang USB port"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Hindi na nakaka-detect ang telepono ng liquid o debris."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Kinukuha ang ulat ng bug…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Gusto mo bang ibahagi ang ulat ng bug?"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index e1fa7da..233eb8e 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Parmak izi simgesi"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"yüz tanıma kilidi donanımı yönet"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Uygulamanın, kullanılacak yüz şablonlarını ekleme ve silme yöntemlerini başlatmasına izin verir."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"yüz tanıma kilidi donanımını kullan"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Uygulamanın yüz tanıma kilidi donanımı kullanmasına izin verir"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Yüz tanıma kilidi"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Yüzünüzü yeniden kaydedin"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Daha iyi tanınmasını sağlamak için lütfen yüzünüzü yeniden kaydedin"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Doğru yüz verileri yakalanamadı. Tekrar deneyin."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Yüz doğrulanamıyor. Donanım kullanılamıyor."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Yüz tanıma kilidini yeniden deneyin."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Yeni yüz verisi depolanamıyor. Önce eski bir tanesini silin."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Yüz işlemi iptal edildi."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Yüz tanıma kilidi kullanıcı tarafından iptal edildi."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Çok fazla deneme yapıldı. Daha sonra tekrar deneyin."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Çok fazla deneme yapıldı. Yüz tanıma kilidi devredışı."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Yüz doğrulanamıyor. Tekrar deneyin."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Yüz tanıma kilidi ayarlamadınız."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Bu cihazda yüz tanıma kilidi desteklenmiyor"</string>
<string name="face_name_template" msgid="7004562145809595384">"Yüz <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Test Bandı Modu\'nu devre dışı bırakmak için cihazı fabrika ayarlarına sıfırlayın."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB bağlantı noktasında sıvı veya toz var"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB bağlantı noktası otomatik olarak devre dışı bırakıldı. Daha fazla bilgi için dokunun."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB bağlantı noktasını kullanabilirsiniz"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Telefon artık sıvıları veya tozları algılamıyor."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Hata raporu alınıyor…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Hata raporu paylaşılsın mı?"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 26de583..6fb4cbd 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -555,15 +555,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Значок відбитка пальця"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"керувати апаратним забезпечення для Фейсконтролю"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Додаток може активувати способи додавання й видалення шаблонів облич."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"використовувати апаратне забезпечення для Фейсконтролю"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Додаток може використовувати апаратне забезпечення для Фейсконтролю"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Фейсконтроль"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Повторно проскануйте обличчя"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Повторно проскануйте обличчя для ефективнішого розпізнавання"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Не вдалося чітко зняти обличчя. Повторіть спробу."</string>
@@ -589,20 +585,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Не вдається перевірити обличчя. Апаратне забезпечення недоступне."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Скористайтеся Фейсконтролем ще раз."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Не вдається зберегти нові дані про обличчя. Видаліть старі."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Дію з обличчям скасовано."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Користувач скасував Фейсконтроль."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Забагато спроб. Повторіть пізніше."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Забагато спроб. Фейсконтроль вимкнено."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Не вдається перевірити обличчя. Повторіть спробу."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Ви не налаштували Фейсконтроль"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"На цьому пристрої не підтримується Фейсконтроль."</string>
<string name="face_name_template" msgid="7004562145809595384">"Обличчя <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1410,8 +1401,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Щоб вимкнути режим автоматизованого тестування, відновіть заводські налаштування."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Рідина або сміття в USB-порту"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB-порт автоматично вимкнено. Торкніться, щоб дізнатися більше."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Можна використовувати USB-порт"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Телефон уже не виявляє рідини або сміття."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Створюється повідомлення про помилку…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Надіслати звіт про помилку?"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 78d4a58..a132989 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"فنگر پرنٹ آئیکن"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"چہرے کے ذریعے غیر مقفل کرنے والے ہارڈ ویئر کا نظم کریں"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"ایپ کو چہرے کی تمثیلات شامل اور حذف کرنے کے طریقوں کو کالعدم قرار دینے کی اجازت دیتا ہے۔"</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"چہرے کے ذریعے غیر مقفل کرنے والا ہارڈ ویئر استعمال کریں"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"ایپ کو تصدیق کے لیے چہرے کے ذریعے غیر مقفل کرنے کا ہارڈ ویئر استعمال کرنے کی اجازت دیتی ہے"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"چہرے کے ذریعے غیر مقفل کریں"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"اپنے چہرے کو دوبارہ مندرج کریں"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"شناخت کو بہتر بنانے کے لیے براہ کرم اپنے چہرے کو دوبارہ مندرج کریں"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"چہرے کا درست ڈيٹا کیپچر نہیں ہو سکا۔ پھر آزمائيں۔"</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"چہرے کی توثیق نہیں کی جا سکی۔ ہارڈ ویئر دستیاب نہیں ہے۔"</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"چہرے کے ذریعے غیر مقفل کرنے کو دوبارہ آزمائیں۔"</string>
<string name="face_error_no_space" msgid="2712120617457553825">"چہرے کا نیا ڈیٹا اسٹور نہیں کر سکتے۔ پہلے پرانا حذف کریں۔"</string>
<string name="face_error_canceled" msgid="283945501061931023">"چہرے پر ہونے والی کارروائی منسوخ ہو گئی۔"</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"صارف نے چہرے کے ذریعے غیر مقفل کرنے کو منسوخ کر دیا۔"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"کافی زیادہ کوششیں کی گئیں۔ دوبارہ کوشش کریں۔"</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"کافی زیادہ کوششیں۔ چہرے کے ذریعے غیر مقفل کرنا غیر فعال کر دیا گیا۔"</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"چہرے کی توثیق نہیں کی جا سکی۔ پھر آزمائيں۔"</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"آپ نے بذریعہ چہرہ غیر مقفل کرنے کو سیٹ نہیں کیا ہے۔"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"اس آلہ پر چہرے کے ذریعے غیر مقفل کرنا تعاون یافتہ نہیں ہے۔"</string>
<string name="face_name_template" msgid="7004562145809595384">"چہرہ <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1367,8 +1358,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"ٹیسٹ ہارنیس موڈ غیر فعال کرنے کے لیے فیکٹری ری سیٹ کریں۔"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB پورٹ میں سیال یا دھول ہے"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB پورٹ خودکار طور پر غیر فعال کر دیا گیا۔ مزید جاننے کیلئے تھپتھپائیں۔"</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB پورٹ کا استعمال ٹھیک ہے"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"فون مزید سیال یا دھول کا پتہ نہیں لگاتا۔"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"بگ رپورٹ لی جا رہی ہے…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"بگ رپورٹ کا اشتراک کریں؟"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index a192c76..7775aa7 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Barmoq izi belgisi"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"yuz bilan ochish qurilmasini boshqarish"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Ilova foydalanish uchun yuz namunalarini qo‘shish va o‘chirish usullarini tatbiq qilishi mumkin."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"yuz bilan ochish qurilmasidan foydalanish"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Haqiqiylikni tekshirish uchun yuz bilan ochishdan foydalanish imkonini beradi"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Yuz bilan ochish"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Yuzingizni yana qayd qiling"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Yuzingiz yanada yaxshiroq aniqlanishi uchun uni yana bir marta qayd qiling"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Yuz ravshan suratga olinmadi. Qaytadan urining."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Yuzingiz tasdiqlanmadi. Qurilma ishlamayapti."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Yana yuz bilan ochishga urining."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Yuzga oid axborot saqlanmadi. Avval eskilari tozalansin."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Yuzni aniqlash bekor qilindi."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Yuz bilan ochishni foydalanuvchi bekor qildi."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Juda ko‘p urinildi. Keyinroq qaytadan urining."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Juda koʻp urinildi. Yuz bilan ochish faolsizlantirildi."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Yuzingiz tasdiqlanmadi. Qaytadan urining."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Hali yuz bilan ochishni sozlamagansiz"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Yuz bilan ochish bu qurilmada ishlamaydi"</string>
<string name="face_name_template" msgid="7004562145809595384">"Yuz <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1367,8 +1358,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Xavfsizlik sinovi rejimini faolsizlantirish uchun zavod sozlamalariga qaytaring."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB portda suyuqlik yoki parcha bor"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB port avtomatik tarzda faolsizlashtirildi. Batafsil axborot olish uchun bosing."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB portdan foydalanish mumkin"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Telefon endi suyuqlik yoki turli parchalarni aniqlay olmaydi."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Xatoliklar hisoboti olinmoqda…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Xatoliklar hisoboti yuborilsinmi?"</string>
@@ -1504,7 +1494,7 @@
<string name="find_next" msgid="5742124618942193978">"Keyingisini topish"</string>
<string name="find_previous" msgid="2196723669388360506">"Oldingisini topish"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"<xliff:g id="NAME">%s</xliff:g>dan manzil haqida so‘rov"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"Manzil so‘rovi"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"Joylashuv axboroti talabi"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"<xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>) tomonidan so‘raldi"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"Ha"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"Yo‘q"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index b4e1559..57c9816 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"Biểu tượng vân tay"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"quản lý phần cứng mở khóa bằng khuôn mặt"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"Cho phép ứng dụng gọi ra các phương pháp để thêm và xóa mẫu khuôn mặt sử dụng."</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"sử dụng phần cứng mở khóa bằng khuôn mặt"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"Cho phép ứng dụng dùng phần cứng mở khóa bằng khuôn mặt để tiến hành xác thực"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"Mở khóa bằng khuôn mặt"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"Đăng ký lại khuôn mặt của bạn"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"Để cải thiện khả năng nhận dạng, hãy đăng ký lại khuôn mặt của bạn"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"Không thể ghi lại đúng dữ liệu mặt. Hãy thử lại."</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"Không thể xác minh khuôn mặt. Phần cứng không có sẵn."</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"Thử lại thao tác mở khóa bằng khuôn mặt."</string>
<string name="face_error_no_space" msgid="2712120617457553825">"Không lưu được dữ liệu khuôn mặt mới. Hãy xóa dữ liệu cũ trước."</string>
<string name="face_error_canceled" msgid="283945501061931023">"Đã hủy thao tác dùng khuôn mặt."</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"Người dùng đã hủy thao tác mở khóa bằng khuôn mặt."</string>
<string name="face_error_lockout" msgid="3407426963155388504">"Bạn đã thử quá nhiều lần. Hãy thử lại sau."</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"Quá nhiều lần thử. Đã tắt tính năng mở khóa bằng khuôn mặt."</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"Không thể xác minh khuôn mặt. Hãy thử lại."</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"Bạn chưa thiết lập tính năng mở khóa bằng khuôn mặt."</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"Thiết bị này không hỗ trợ tính năng mở khóa bằng khuôn mặt."</string>
<string name="face_name_template" msgid="7004562145809595384">"Khuôn mặt <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -692,7 +683,7 @@
<string name="policydesc_disableKeyguardFeatures" msgid="2044755691354158439">"Ngăn sử dụng một số tính năng của phương thức khóa màn hình."</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Nhà riêng"</item>
- <item msgid="869923650527136615">"Di Động"</item>
+ <item msgid="869923650527136615">"Di động"</item>
<item msgid="7897544654242874543">"Cơ quan"</item>
<item msgid="1103601433382158155">"Số fax cơ quan"</item>
<item msgid="1735177144948329370">"Số fax nhà riêng"</item>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"Khôi phục cài đặt gốc để tắt Chế độ khai thác kiểm thử."</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"Có chất lỏng hoặc mảnh vỡ trong cổng USB"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"Cổng USB đã tự động tắt. Nhấn để tìm hiểu thêm."</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"Có thể sử dụng cổng USB"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"Điện thoại không còn phát hiện chất lỏng hay mảnh vỡ nữa."</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"Đang thu thập báo cáo lỗi…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"Chia sẻ báo cáo lỗi?"</string>
@@ -1503,7 +1493,7 @@
<string name="find_next" msgid="5742124618942193978">"Tìm kết quả phù hợp tiếp theo"</string>
<string name="find_previous" msgid="2196723669388360506">"Tìm kết quả phù hợp trước"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"Yêu cầu vị trí từ <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"Yêu cầu vị trí"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"Yêu cầu truy cập vị trí"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"Được yêu cầu bởi <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"Có"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"Không"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index da239b9..ad08e53 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"指纹图标"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"管理人脸解锁硬件"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"允许该应用调用方法来添加和删除可用的人脸模板。"</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"使用人脸解锁硬件"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"允许该应用使用人脸解锁硬件进行身份验证"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"人脸解锁"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"重新注册您的面孔"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"要提升识别精确度,请重新注册您的面孔"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"无法捕获准确的人脸数据,请重试。"</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"无法验证人脸。硬件无法使用。"</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"请重新尝试人脸解锁。"</string>
<string name="face_error_no_space" msgid="2712120617457553825">"无法存储新的人脸数据。请先删除旧的人脸数据。"</string>
<string name="face_error_canceled" msgid="283945501061931023">"面孔处理操作已取消。"</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"用户已取消人脸解锁。"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"尝试次数过多,请稍后重试。"</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"尝试次数过多,人脸解锁已停用。"</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"无法验证人脸,请重试。"</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"您尚未设置人脸解锁。"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"此设备不支持人脸解锁。"</string>
<string name="face_name_template" msgid="7004562145809595384">"面孔 <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"恢复出厂设置以停用自动化测试框架模式。"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB 端口中有液体或碎屑"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB 端口已自动停用。点按即可了解详情。"</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"允许使用 USB 端口"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"手机不再检测到液体或碎屑。"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"正在生成错误报告…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"要分享错误报告吗?"</string>
@@ -1503,7 +1493,7 @@
<string name="find_next" msgid="5742124618942193978">"下一条结果"</string>
<string name="find_previous" msgid="2196723669388360506">"上一条结果"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"来自<xliff:g id="NAME">%s</xliff:g>的定位请求"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"定位请求"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"位置信息请求"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"请求人:<xliff:g id="NAME">%1$s</xliff:g>(<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"是"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"否"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 1da26f0..2760535 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"指紋圖示"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"管理臉容解鎖硬件"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"允許應用程式調用方法,以加入和刪除可用的臉孔範本。"</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"使用臉容解鎖硬件"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"允許應用程式使用臉容解鎖硬件來驗證"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"臉容解鎖"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"重新註冊臉孔"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"如要提高識別能力,請重新註冊您的臉孔"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"無法擷取準確的臉容資料。請再試一次。"</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"無法驗證臉孔,硬件無法使用。"</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"請再次嘗試「臉容解鎖」。"</string>
<string name="face_error_no_space" msgid="2712120617457553825">"無法儲存新的臉容資料,請先刪除舊資料。"</string>
<string name="face_error_canceled" msgid="283945501061931023">"臉孔操作已取消。"</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"使用者已取消「臉容解鎖」。"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"嘗試次數過多,請稍後再試。"</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"嘗試次數過多,「臉容解鎖」已停用。"</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"無法驗證臉孔。請再試一次。"</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"您尚未設定「臉容解鎖」。"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"此裝置不支援「臉容解鎖」。"</string>
<string name="face_name_template" msgid="7004562145809595384">"臉孔 <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"請將裝置回復原廠設定,以停用測試工具模式。"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB 連接埠中有液體或碎片"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"USB 連接埠已自動停用。輕按即可瞭解詳情。"</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"USB 連接埠可安全使用"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"手機不再偵測到液體或碎片。"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"正在取得錯誤報告…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"要分享錯誤報告嗎?"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 2e1a697..59e52fc 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -549,15 +549,11 @@
<string-array name="fingerprint_error_vendor">
</string-array>
<string name="fingerprint_icon_content_description" msgid="2340202869968465936">"指紋圖示"</string>
- <!-- no translation found for permlab_manageFace (7262837876352591553) -->
- <skip />
+ <string name="permlab_manageFace" msgid="7262837876352591553">"管理人臉解鎖硬體"</string>
<string name="permdesc_manageFace" msgid="8919637120670185330">"允許應用程式呼叫方法來新增及移除可用的臉孔範本。"</string>
- <!-- no translation found for permlab_useFaceAuthentication (2565716575739037572) -->
- <skip />
- <!-- no translation found for permdesc_useFaceAuthentication (4712947955047607722) -->
- <skip />
- <!-- no translation found for face_recalibrate_notification_name (1913676850645544352) -->
- <skip />
+ <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"使用人臉解鎖硬體"</string>
+ <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"允許應用程式使用人臉解鎖硬體進行驗證"</string>
+ <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"人臉解鎖"</string>
<string name="face_recalibrate_notification_title" msgid="4087620069451499365">"請重新註冊你的臉孔"</string>
<string name="face_recalibrate_notification_content" msgid="5530308842361499835">"為提升辨識精準度,請重新註冊你的臉孔"</string>
<string name="face_acquired_insufficient" msgid="2767330364802375742">"無法擷取精準臉孔資料,請再試一次。"</string>
@@ -583,20 +579,15 @@
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="396883585636963908">"相關硬體無法使用,因此無法驗證臉孔。"</string>
- <!-- no translation found for face_error_timeout (981512090365729465) -->
- <skip />
+ <string name="face_error_timeout" msgid="981512090365729465">"請重新進行人臉解鎖。"</string>
<string name="face_error_no_space" msgid="2712120617457553825">"無法儲存新的臉孔資料,請先刪除舊的資料。"</string>
<string name="face_error_canceled" msgid="283945501061931023">"臉孔處理作業已取消。"</string>
- <!-- no translation found for face_error_user_canceled (5317030072349668946) -->
- <skip />
+ <string name="face_error_user_canceled" msgid="5317030072349668946">"使用者已取消人臉解鎖作業。"</string>
<string name="face_error_lockout" msgid="3407426963155388504">"嘗試次數過多,請稍後再試。"</string>
- <!-- no translation found for face_error_lockout_permanent (4723594314443097159) -->
- <skip />
+ <string name="face_error_lockout_permanent" msgid="4723594314443097159">"嘗試次數過多,因此系統已停用人臉解鎖功能。"</string>
<string name="face_error_unable_to_process" msgid="4940944939691171539">"無法驗證臉孔,請再試一次。"</string>
- <!-- no translation found for face_error_not_enrolled (4016937174832839540) -->
- <skip />
- <!-- no translation found for face_error_hw_not_present (8302690289757559738) -->
- <skip />
+ <string name="face_error_not_enrolled" msgid="4016937174832839540">"你尚未設定人臉解鎖功能。"</string>
+ <string name="face_error_hw_not_present" msgid="8302690289757559738">"這個裝置不支援人臉解鎖功能。"</string>
<string name="face_name_template" msgid="7004562145809595384">"臉孔 <xliff:g id="FACEID">%d</xliff:g>"</string>
<string-array name="face_error_vendor">
</string-array>
@@ -1366,8 +1357,7 @@
<string name="test_harness_mode_notification_message" msgid="1343197173054407119">"恢復原廠設定以停用測試控管工具模式。"</string>
<string name="usb_contaminant_detected_title" msgid="7136400633704058349">"USB 連接埠中有液體或灰塵"</string>
<string name="usb_contaminant_detected_message" msgid="832337061059487250">"系統已自動停用 USB 連接埠。輕觸即可瞭解詳情。"</string>
- <!-- no translation found for usb_contaminant_not_detected_title (7708281124088684821) -->
- <skip />
+ <string name="usb_contaminant_not_detected_title" msgid="7708281124088684821">"現在可以使用 USB 連接埠"</string>
<string name="usb_contaminant_not_detected_message" msgid="2415791798244545292">"手機目前無法偵測液體或灰塵。"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"正在接收錯誤報告…"</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"要分享錯誤報告嗎?"</string>
@@ -1503,7 +1493,7 @@
<string name="find_next" msgid="5742124618942193978">"尋找下一個項目"</string>
<string name="find_previous" msgid="2196723669388360506">"尋找上一個項目"</string>
<string name="gpsNotifTicker" msgid="5622683912616496172">"<xliff:g id="NAME">%s</xliff:g> 的地點要求"</string>
- <string name="gpsNotifTitle" msgid="5446858717157416839">"位置要求"</string>
+ <string name="gpsNotifTitle" msgid="5446858717157416839">"位置資訊要求"</string>
<string name="gpsNotifMessage" msgid="1374718023224000702">"要求者:<xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SERVICE">%2$s</xliff:g>)"</string>
<string name="gpsVerifYes" msgid="2346566072867213563">"是"</string>
<string name="gpsVerifNo" msgid="1146564937346454865">"否"</string>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 048f341..3b28265 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -218,4 +218,9 @@
<color name="chooser_row_divider">@color/list_divider_color_light</color>
<color name="chooser_gradient_background">@color/loading_gradient_background_color_light</color>
<color name="chooser_gradient_highlight">@color/loading_gradient_highlight_color_light</color>
+
+ <color name="GM2_grey_800">#ff3C4043</color>
+
+ <!-- Resolver/Chooser -->
+ <color name="resolver_text_color_secondary_dark">#ffC4C6C6</color>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a7433d0..a467f40 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2283,7 +2283,7 @@
effectively and terminate the dream. Use -1 to disable this safety feature. -->
<integer name="config_dreamsBatteryLevelDrainCutoff">5</integer>
<!-- Limit of how long the device can remain unlocked due to attention checking. -->
- <integer name="config_attentionMaximumExtension">240000</integer> <!-- 4 minutes -->
+ <integer name="config_attentionMaximumExtension">330000</integer> <!-- 5 minutes and 30 sec.-->
<!-- How long we should wait until we give up on receiving an attention API callback. -->
<integer name="config_attentionApiTimeout">2000</integer> <!-- 2 seconds -->
@@ -3902,6 +3902,10 @@
{@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} -->
<string name="config_secondaryHomeComponent" translatable="false">com.android.launcher3/com.android.launcher3.SecondaryDisplayLauncher</string>
+ <!-- Force secondary home launcher specified in config_secondaryHomeComponent always. If this is
+ not set, secondary home launcher can be replaced by user. -->
+ <bool name ="config_useSystemProvidedLauncherForSecondary">false</bool>
+
<!-- If device supports corner radius on windows.
This should be turned off on low-end devices to improve animation performance. -->
<bool name="config_supportsRoundedCornersOnWindows">true</bool>
@@ -4064,6 +4068,9 @@
for higher refresh rates to be automatically used out of the box -->
<integer name="config_defaultPeakRefreshRate">60</integer>
+ <!-- The default brightness threshold that allows to switch to higher refresh rate -->
+ <integer name="config_brightnessThresholdOfPeakRefreshRate">-1</integer>
+
<!-- The type of the light sensor to be used by the display framework for things like
auto-brightness. If unset, then it just gets the default sensor of type TYPE_LIGHT. -->
<string name="config_displayLightSensorType" translatable="false" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index fe49a31..61b3bc8 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -252,12 +252,15 @@
<item>@string/wfcSpnFormat_wifi</item>
<item>@string/wfcSpnFormat_wifi_calling_wo_hyphen</item>
<item>@string/wfcSpnFormat_vowifi</item>
+ <item>@string/wfcSpnFormat_spn_wifi_calling_vo_hyphen</item>
</string-array>
<!-- Spn during Wi-Fi Calling: "<operator>" -->
<string name="wfcSpnFormat_spn"><xliff:g id="spn" example="Operator">%s</xliff:g></string>
<!-- Spn during Wi-Fi Calling: "<operator> Wi-Fi Calling" -->
<string name="wfcSpnFormat_spn_wifi_calling"><xliff:g id="spn" example="Operator">%s</xliff:g> Wi-Fi Calling</string>
+ <!-- Spn during Wi-Fi Calling: "<operator> WiFi Calling" -->
+ <string name="wfcSpnFormat_spn_wifi_calling_vo_hyphen"><xliff:g id="spn" example="Operator">%s</xliff:g> WiFi Calling</string>
<!-- Spn during Wi-Fi Calling: "WLAN Call" -->
<string name="wfcSpnFormat_wlan_call">WLAN Call</string>
<!-- Spn during Wi-Fi Calling: "<operator> WLAN Call" -->
@@ -4495,10 +4498,18 @@
<xliff:g id="service_name" example="TalkBack">%1$s</xliff:g></string>
<!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button in the navigation bar. -->
- <string name="accessibility_button_prompt_text">Choose a feature to use when you tap the Accessibility button:</string>
+ <string name="accessibility_button_prompt_text">Choose a service to use when you tap the accessibility button:</string>
+ <!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button when gesture navigation is enabled [CHAR LIMIT=none] -->
+ <string name="accessibility_gesture_prompt_text">Choose a service to use with the accessibility gesture (swipe up from the bottom of the screen with two fingers):</string>
+ <!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button when gesture navigation and TalkBack is enabled [CHAR LIMIT=none] -->
+ <string name="accessibility_gesture_3finger_prompt_text">Choose a service to use with the accessibility gesture (swipe up from the bottom of the screen with three fingers):</string>
<!-- Text describing how to display UI allowing a user to select a target service or feature to be assigned to the Accessibility button in the navigation bar. -->
- <string name="accessibility_button_instructional_text">To change features, touch & hold the Accessibility button.</string>
+ <string name="accessibility_button_instructional_text">To switch between services, touch & hold the accessibility button.</string>
+ <!-- Text describing how to display UI allowing a user to select a target service or feature to be assigned to the Accessibility button when gesture navigation is enabled. [CHAR LIMIT=none] -->
+ <string name="accessibility_gesture_instructional_text">To switch between services, swipe up with two fingers and hold.</string>
+ <!-- Text describing how to display UI allowing a user to select a target service or feature to be assigned to the Accessibility button when gesture navigation and TalkBack is enabled. [CHAR LIMIT=none] -->
+ <string name="accessibility_gesture_3finger_instructional_text">To switch between services, swipe up with three fingers and hold.</string>
<!-- Text used to describe system navigation features, shown within a UI allowing a user to assign system magnification features to the Accessibility button in the navigation bar. -->
<string name="accessibility_magnification_chooser_text">Magnification</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ff5845f..8b14da5 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3282,9 +3282,16 @@
<java-symbol type="layout" name="accessibility_button_chooser_item" />
<java-symbol type="id" name="accessibility_button_chooser_grid" />
<java-symbol type="id" name="accessibility_button_prompt" />
+ <java-symbol type="id" name="accessibility_button_prompt_prologue" />
<java-symbol type="id" name="accessibility_button_target_icon" />
<java-symbol type="id" name="accessibility_button_target_label" />
<java-symbol type="string" name="accessibility_magnification_chooser_text" />
+
+ <java-symbol type="string" name="accessibility_gesture_prompt_text" />
+ <java-symbol type="string" name="accessibility_gesture_3finger_prompt_text" />
+ <java-symbol type="string" name="accessibility_gesture_instructional_text" />
+ <java-symbol type="string" name="accessibility_gesture_3finger_instructional_text" />
+
<java-symbol type="drawable" name="ic_accessibility_magnification" />
<!-- com.android.internal.widget.RecyclerView -->
@@ -3695,6 +3702,7 @@
<!-- For Secondary Launcher -->
<java-symbol type="string" name="config_secondaryHomeComponent" />
+ <java-symbol type="bool" name="config_useSystemProvidedLauncherForSecondary" />
<java-symbol type="string" name="battery_saver_notification_channel_name" />
<java-symbol type="string" name="battery_saver_sticky_disabled_notification_title" />
@@ -3766,6 +3774,7 @@
<!-- For high refresh rate displays -->
<java-symbol type="integer" name="config_defaultPeakRefreshRate" />
+ <java-symbol type="integer" name="config_brightnessThresholdOfPeakRefreshRate" />
<!-- For Auto-Brightness -->
<java-symbol type="string" name="config_displayLightSensorType" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index fda37c8..a76804f 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -265,6 +265,7 @@
Settings.Global.EUICC_PROVISIONED,
Settings.Global.EUICC_SUPPORTED_COUNTRIES,
Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS,
+ Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS,
Settings.Global.FANCY_IME_ANIMATIONS,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
Settings.Global.FORCED_APP_STANDBY_ENABLED,
diff --git a/core/tests/coretests/src/android/view/ViewRootSurfaceCallbackTest.java b/core/tests/coretests/src/android/view/ViewRootSurfaceCallbackTest.java
new file mode 100644
index 0000000..94917cf
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ViewRootSurfaceCallbackTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static android.os.Process.myTid;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.os.HandlerThread;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+public class ViewRootSurfaceCallbackTest implements SurfaceHolder.Callback2 {
+ private static final String TAG = ViewRootSurfaceCallbackTest.class.getSimpleName();
+ private static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(3);
+
+ @Rule
+ public ActivityTestRule<Activity> mActivityRule = new ActivityTestRule<>(Activity.class);
+
+ private final CompletableFuture<Integer> mTidOfSurfaceCreated = new CompletableFuture<>();
+ private final CompletableFuture<Integer> mTidOfSurfaceDestroyed = new CompletableFuture<>();
+
+ private boolean mDuplicatedSurfaceDestroyed;
+
+ /**
+ * Verifies that the calling thread of {@link SurfaceHolder.Callback2} should be the same as the
+ * thread that created the view root.
+ */
+ @Test
+ public void testCallingTidOfSurfaceCallback() throws Exception {
+ final Activity activity = mActivityRule.getActivity();
+ activity.setTurnScreenOn(true);
+ activity.setShowWhenLocked(true);
+
+ // Create a dialog that runs on another thread and let it handle surface by itself.
+ final HandlerThread thread = new HandlerThread(TAG);
+ thread.start();
+ thread.getThreadHandler().runWithScissors(() -> {
+ final AlertDialog dialog = new AlertDialog.Builder(activity)
+ .setTitle(TAG).setMessage(TAG).create();
+ dialog.getWindow().takeSurface(this);
+ dialog.show();
+ }, TIMEOUT_MS);
+ final int attachedTid = thread.getThreadId();
+
+ assertEquals(attachedTid,
+ mTidOfSurfaceCreated.get(TIMEOUT_MS, TimeUnit.MILLISECONDS).intValue());
+
+ // Make the activity invisible.
+ activity.moveTaskToBack(true /* nonRoot */);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+ assertEquals(attachedTid,
+ mTidOfSurfaceDestroyed.get(TIMEOUT_MS, TimeUnit.MILLISECONDS).intValue());
+ assertFalse("surfaceDestroyed should not be called twice", mDuplicatedSurfaceDestroyed);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ mTidOfSurfaceCreated.complete(myTid());
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ if (!mTidOfSurfaceDestroyed.isDone()) {
+ mTidOfSurfaceDestroyed.complete(myTid());
+ } else {
+ mDuplicatedSurfaceDestroyed = true;
+ }
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ }
+
+ @Override
+ public void surfaceRedrawNeeded(SurfaceHolder holder) {
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java
index cc0ddb7..cd61011 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryTest.java
@@ -22,25 +22,28 @@
import android.content.Context;
import android.os.Parcel;
+import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
/**
* Test BatteryStatsHistory.
*/
@RunWith(AndroidJUnit4.class)
public class BatteryStatsHistoryTest {
+ private static final String TAG = "BatteryStatsHistoryTest";
private static final int MAX_HISTORY_FILES = 32;
private final BatteryStatsImpl mBatteryStatsImpl = new MockBatteryStatsImpl();
private final Parcel mHistoryBuffer = Parcel.obtain();
@@ -53,6 +56,12 @@
Context context = InstrumentationRegistry.getContext();
mSystemDir = context.getDataDir();
mHistoryDir = new File(mSystemDir, BatteryStatsHistory.HISTORY_DIR);
+ String[] files = mHistoryDir.list();
+ if (files != null) {
+ for (int i = 0; i < files.length; i++) {
+ new File(mHistoryDir, files[i]).delete();
+ }
+ }
mHistoryDir.delete();
}
@@ -60,28 +69,32 @@
public void testConstruct() {
BatteryStatsHistory history =
new BatteryStatsHistory(mBatteryStatsImpl, mSystemDir, mHistoryBuffer);
+ createActiveFile(history);
verifyFileNumbers(history, Arrays.asList(0));
verifyActiveFile(history, "0.bin");
}
@Test
- public void testCreateNextFile() {
+ public void testStartNextFile() {
BatteryStatsHistory history =
new BatteryStatsHistory(mBatteryStatsImpl, mSystemDir, mHistoryBuffer);
List<Integer> fileList = new ArrayList<>();
fileList.add(0);
+ createActiveFile(history);
// create file 1 to 31.
for (int i = 1; i < MAX_HISTORY_FILES; i++) {
fileList.add(i);
- history.createNextFile();
+ history.startNextFile();
+ createActiveFile(history);
verifyFileNumbers(history, fileList);
verifyActiveFile(history, i + ".bin");
}
// create file 32
- history.createNextFile();
+ history.startNextFile();
+ createActiveFile(history);
fileList.add(32);
fileList.remove(0);
// verify file 0 is deleted.
@@ -90,7 +103,8 @@
verifyActiveFile(history, "32.bin");
// create file 33
- history.createNextFile();
+ history.startNextFile();
+ createActiveFile(history);
// verify file 1 is deleted
fileList.add(33);
fileList.remove(0);
@@ -108,6 +122,7 @@
verifyActiveFile(history2, "33.bin");
history2.resetAllFiles();
+ createActiveFile(history2);
// verify all existing files are deleted.
for (int i = 2; i < 33; ++i) {
verifyFileDeleted(i + ".bin");
@@ -118,7 +133,8 @@
verifyActiveFile(history2, "0.bin");
// create file 1.
- history2.createNextFile();
+ history2.startNextFile();
+ createActiveFile(history2);
verifyFileNumbers(history2, Arrays.asList(0, 1));
verifyActiveFile(history2, "1.bin");
}
@@ -142,4 +158,13 @@
private void verifyFileDeleted(String file) {
assertFalse(new File(mHistoryDir, file).exists());
}
+
+ private void createActiveFile(BatteryStatsHistory history) {
+ final File file = history.getActiveFile().getBaseFile();
+ try {
+ file.createNewFile();
+ } catch (IOException e) {
+ Log.e(TAG, "Error creating history file " + file.getPath(), e);
+ }
+ }
}
\ No newline at end of file
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 987db8b..fde0e64 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -990,9 +990,9 @@
public static native int setMasterBalance(float balance);
// helpers for android.media.AudioManager.getProperty(), see description there for meaning
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 112561552)
+ @UnsupportedAppUsage(trackingBug = 134049522)
public static native int getPrimaryOutputSamplingRate();
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 112561552)
+ @UnsupportedAppUsage(trackingBug = 134049522)
public static native int getPrimaryOutputFrameCount();
@UnsupportedAppUsage
public static native int getOutputLatency(int stream);
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 112ce1d..c43b78c 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -869,7 +869,7 @@
* This key retrieves the location information, if available.
* The location should be specified according to ISO-6709 standard, under
* a mp4/3gp box "@xyz". Location with longitude of -90 degrees and latitude
- * of 180 degrees will be retrieved as "-90.0000+180.0000", for instance.
+ * of 180 degrees will be retrieved as "+180.0000-90.0000/", for instance.
*/
public static final int METADATA_KEY_LOCATION = 23;
/**
diff --git a/packages/CaptivePortalLogin/Android.bp b/packages/CaptivePortalLogin/Android.bp
deleted file mode 100644
index 1f6c2ae..0000000
--- a/packages/CaptivePortalLogin/Android.bp
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-// Copyright (C) 2019 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.
-//
-
-java_defaults {
- name: "CaptivePortalLoginDefaults",
- srcs: ["src/**/*.java"],
- sdk_version: "system_current",
- min_sdk_version: "28",
- static_libs: [
- "androidx.legacy_legacy-support-v4",
- "metrics-constants-protos",
- "captiveportal-lib",
- ],
- manifest: "AndroidManifest.xml",
-}
-
-android_app {
- name: "CaptivePortalLogin",
- defaults: ["CaptivePortalLoginDefaults"],
- certificate: "networkstack",
-}
-
-// Alternative CaptivePortalLogin signed with the platform cert, to use
-// with InProcessNetworkStack.
-android_app {
- name: "PlatformCaptivePortalLogin",
- defaults: ["CaptivePortalLoginDefaults"],
- certificate: "platform",
- overrides: ["CaptivePortalLogin"],
-}
diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml
deleted file mode 100644
index 86d6d44..0000000
--- a/packages/CaptivePortalLogin/AndroidManifest.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.captiveportallogin"
- android:versionCode="220000000"
- android:versionName="Q-initial">
-
- <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
- <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" />
- <uses-permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS" />
- <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" />
-
- <application android:label="@string/app_name"
- android:icon="@drawable/app_icon"
- android:usesCleartextTraffic="true"
- android:supportsRtl="true" >
- <activity
- android:name="com.android.captiveportallogin.CaptivePortalLoginActivity"
- android:label="@string/action_bar_label"
- android:theme="@style/AppTheme"
- android:configChanges="keyboardHidden|orientation|screenSize" >
- <intent-filter>
- <action android:name="android.net.conn.CAPTIVE_PORTAL"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/packages/CaptivePortalLogin/OWNERS b/packages/CaptivePortalLogin/OWNERS
deleted file mode 100644
index d3836d4..0000000
--- a/packages/CaptivePortalLogin/OWNERS
+++ /dev/null
@@ -1,8 +0,0 @@
-set noparent
-
-codewiz@google.com
-jchalard@google.com
-junyulai@google.com
-lorenzo@google.com
-reminv@google.com
-satk@google.com
diff --git a/packages/CaptivePortalLogin/assets/quantum_ic_warning_amber_96.png b/packages/CaptivePortalLogin/assets/quantum_ic_warning_amber_96.png
deleted file mode 100644
index 08294ce..0000000
--- a/packages/CaptivePortalLogin/assets/quantum_ic_warning_amber_96.png
+++ /dev/null
Binary files differ
diff --git a/packages/CaptivePortalLogin/res/drawable/app_icon.xml b/packages/CaptivePortalLogin/res/drawable/app_icon.xml
deleted file mode 100644
index 456ca83..0000000
--- a/packages/CaptivePortalLogin/res/drawable/app_icon.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
- Copyright (C) 2019 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.
--->
-<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
- <background>
- <color android:color="@*android:color/accent_device_default_light" />
- </background>
- <foreground>
- <inset
- android:drawable="@drawable/maybe_wifi"
- android:inset="25%">
- </inset>
- </foreground>
-</adaptive-icon>
diff --git a/packages/CaptivePortalLogin/res/drawable/maybe_wifi.xml b/packages/CaptivePortalLogin/res/drawable/maybe_wifi.xml
deleted file mode 100644
index 207aade..0000000
--- a/packages/CaptivePortalLogin/res/drawable/maybe_wifi.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<!--
-Copyright (C) 2019 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="26.0dp"
- android:height="24.0dp"
- android:viewportWidth="26.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#4DFFFFFF"
- android:pathData="M19.1,14l-3.4,0l0,-1.5c0,-1.8 0.8,-2.8 1.5,-3.4C18.1,8.3 19.200001,8 20.6,8c1.2,0 2.3,0.3 3.1,0.8l1.9,-2.3C25.1,6.1 20.299999,2.1 13,2.1S0.9,6.1 0.4,6.5L13,22l0,0l0,0l0,0l0,0l6.5,-8.1L19.1,14z"/>
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M19.5,17.799999c0,-0.8 0.1,-1.3 0.2,-1.6c0.2,-0.3 0.5,-0.7 1.1,-1.2c0.4,-0.4 0.7,-0.8 1,-1.1s0.4,-0.8 0.4,-1.2c0,-0.5 -0.1,-0.9 -0.4,-1.2c-0.3,-0.3 -0.7,-0.4 -1.2,-0.4c-0.4,0 -0.8,0.1 -1.1,0.3c-0.3,0.2 -0.4,0.6 -0.4,1.1l-1.9,0c0,-1 0.3,-1.7 1,-2.2c0.6,-0.5 1.5,-0.8 2.5,-0.8c1.1,0 2,0.3 2.6,0.8c0.6,0.5 0.9,1.3 0.9,2.3c0,0.7 -0.2,1.3 -0.6,1.8c-0.4,0.6 -0.9,1.1 -1.5,1.6c-0.3,0.3 -0.5,0.5 -0.6,0.7c-0.1,0.2 -0.1,0.6 -0.1,1L19.5,17.700001zM21.4,21l-1.9,0l0,-1.8l1.9,0L21.4,21z"/>
-</vector>
diff --git a/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml b/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml
deleted file mode 100644
index f095d4e..0000000
--- a/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context="com.android.captiveportallogin.CaptivePortalLoginActivity"
- tools:ignore="MergeRootFrame" >
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
-
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="4dp" >
-
- <!-- Eliminates ProgressBar padding by boxing it into a 4dp high container -->
- <ProgressBar
- android:id="@+id/progress_bar"
- style="@android:style/Widget.Material.Light.ProgressBar.Horizontal"
- android:indeterminate="false"
- android:max="100"
- android:progress="0"
- android:layout_gravity="center"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- </FrameLayout>
-
- <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
- android:id="@+id/swipe_refresh"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <WebView
- android:id="@+id/webview"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_alignParentBottom="false"
- android:layout_alignParentRight="false" />
- </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
-
- </LinearLayout>
-</FrameLayout>
diff --git a/packages/CaptivePortalLogin/res/layout/ssl_warning.xml b/packages/CaptivePortalLogin/res/layout/ssl_warning.xml
deleted file mode 100644
index ce05e78..0000000
--- a/packages/CaptivePortalLogin/res/layout/ssl_warning.xml
+++ /dev/null
@@ -1,97 +0,0 @@
-<?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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <!-- ssl error type -->
- <TextView
- android:id="@+id/ssl_error_type"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="start"
- android:text="SSL_UNKNOWN"
- android:layout_marginStart="24dip"
- android:layout_marginEnd="24dip"
- android:layout_marginBottom="0dip"
- android:layout_marginTop="24dip" />
-
- <!-- Page info: -->
- <TextView
- android:id="@+id/page_info"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/page_info"
- android:textStyle="bold"
- android:layout_marginStart="24dip"
- android:layout_marginEnd="24dip" />
-
- <!-- Title: -->
- <TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textStyle="bold"
- android:layout_marginStart="24dip"
- android:layout_marginEnd="24dip" />
-
- <!-- Address: -->
- <TextView
- android:id="@+id/address_header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/page_info_address"
- android:layout_marginStart="24dip"
- android:layout_marginEnd="24dip" />
-
- <TextView
- android:id="@+id/address"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="24dip"
- android:layout_marginEnd="24dip" />
-
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="4dip"
- android:paddingEnd="4dip" >
-
- <!-- certificate view: -->
- <LinearLayout
- android:id="@+id/certificate_layout"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_marginBottom="16dip" >
- <TextView
- android:id="@+id/ssl_error_msg"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_marginStart="20dip"
- android:layout_marginEnd="20dip"
- android:gravity="center_vertical"
- android:layout_marginBottom="4dip"
- android:layout_marginTop="16dip" />
- </LinearLayout>
-
- </ScrollView>
-
-</LinearLayout>
diff --git a/packages/CaptivePortalLogin/res/menu/captive_portal_login.xml b/packages/CaptivePortalLogin/res/menu/captive_portal_login.xml
deleted file mode 100644
index 1a88c5c..0000000
--- a/packages/CaptivePortalLogin/res/menu/captive_portal_login.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- tools:context="com.android.captiveportallogin.CaptivePortalLoginActivity" >
- <item
- android:id="@+id/action_do_not_use_network"
- android:orderInCategory="100"
- android:showAsAction="never"
- android:title="@string/action_do_not_use_network"/>
- <item
- android:id="@+id/action_use_network"
- android:orderInCategory="200"
- android:showAsAction="never"
- android:title="@string/action_use_network"/>
-
-</menu>
diff --git a/packages/CaptivePortalLogin/res/values-af/strings.xml b/packages/CaptivePortalLogin/res/values-af/strings.xml
deleted file mode 100644
index 37acf45..0000000
--- a/packages/CaptivePortalLogin/res/values-af/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortal-aanmelding"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Gebruik hierdie netwerk nes dit is"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Moenie hierdie netwerk gebruik nie"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Meld by netwerk aan"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Meld aan by %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Die netwerk waarby jy probeer aansluit, het sekuriteitkwessies."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Byvoorbeeld, die aanmeldbladsy behoort dalk nie aan die organisasie wat gewys word nie."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Gaan in elk geval deur blaaier voort"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Hierdie sertifikaat is nie van \'n betroubare owerheid nie."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Die naam van die werf stem nie ooreen met die naam op die sertifikaat nie."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Hierdie sertifikaat het verval."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Hierdie sertifikaat is nog nie geldig nie."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Hierdie sertifikaat het \'n ongeldige datum."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Hierdie sertifikaat is ongeldig."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Onbekende sertifikaatfout."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Sekuriteitswaarskuwing"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Bekyk sertifikaat"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adres:"</string>
- <string name="page_info" msgid="4416941086705172545">"Bladsy-inligting"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-am/strings.xml b/packages/CaptivePortalLogin/res/values-am/strings.xml
deleted file mode 100644
index 4e35e19..0000000
--- a/packages/CaptivePortalLogin/res/values-am/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"ይህን አውታረ መረብ እንዳለ ተጠቀምበት"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"ይህን አውታረ መረብ አትጠቀምበት"</string>
- <string name="action_bar_label" msgid="917235635415966620">"ወደ አውታረ መረብ በመለያ ይግቡ"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"ወደ %1$s ይግቡ"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"ለመቀላቀል እየሞከሩ ያሉት አውታረ መረብ የደህንነት ችግሮች አሉበት።"</string>
- <string name="ssl_error_example" msgid="647898534624078900">"ለምሳሌ፣ የመግቢያ ገጹ የሚታየው ድርጅት ላይሆን ይችላል።"</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"ለማንኛውም በአሳሽ በኩል ይቀጥሉ"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"ይህ የእውቅና ማረጋገጫ ከታማኝ ቦታ አይደለም።"</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"የጣቢያው ስም ከእውቅና ማረጋገጫው ስም ጋር አይዛመድም።"</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"ይህ የእውቅና ማረጋገጫ ጊዜው አልፏል።"</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ይህ የእውቅና ማረጋገጫ ገና የሚሰራ አይደለም።"</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"ይህ የእውቅና ማረጋገጫ ልክ ያልሆነ ቀን ነው ያለው።"</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"ይህ የእውቅና ማረጋገጫ ልክ ያልሆነ ነው።"</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"ያልታወቀ የእውቅና ማረጋገጫ ስህተት።"</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"የደህንነት ቅንብሮች"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"የእውቅና ማረጋገጫን ይመልከቱ"</string>
- <string name="ok" msgid="2817931639040794018">"እሺ"</string>
- <string name="page_info_address" msgid="1261481517455692363">"አድራሻ፦"</string>
- <string name="page_info" msgid="4416941086705172545">"የገጽ መረጃ"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ar/strings.xml b/packages/CaptivePortalLogin/res/values-ar/strings.xml
deleted file mode 100644
index 9f3aefa..0000000
--- a/packages/CaptivePortalLogin/res/values-ar/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"استخدام هذه الشبكة كما هي"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"عدم استخدام هذه الشبكة"</string>
- <string name="action_bar_label" msgid="917235635415966620">"تسجيل الدخول إلى الشبكة"</string>
- <!-- String.format failed for translation -->
- <!-- no translation found for action_bar_title (5645564790486983117) -->
- <skip />
- <string name="ssl_error_warning" msgid="6653188881418638872">"الشبكة التي تحاول الانضمام إليها بها مشاكل أمنية."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"على سبيل المثال، قد لا تنتمي صفحة تسجيل الدخول إلى المنظمة المعروضة."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"المتابعة على أي حال عبر المتصفح"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"هذه الشهادة ليست من جهة موثوق بها."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"لا يتطابق اسم الموقع مع الاسم على الشهادة."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"انتهت صلاحية هذه الشهادة."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"هذه الشهادة ليست صالحة بعد."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"تشتمل هذه الشهادة على تاريخ غير صالح."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"هذه الشهادة غير صالحة."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"حدث خطأ غير معروف بالشهادة."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"تحذير أمني"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"عرض الشهادة"</string>
- <string name="ok" msgid="2817931639040794018">"حسنًا"</string>
- <string name="page_info_address" msgid="1261481517455692363">"العنوان:"</string>
- <string name="page_info" msgid="4416941086705172545">"معلومات الصفحة"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-as/strings.xml b/packages/CaptivePortalLogin/res/values-as/strings.xml
deleted file mode 100644
index 696483c..0000000
--- a/packages/CaptivePortalLogin/res/values-as/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"এই নেটৱৰ্কটো এইদৰে ব্যৱহাৰ কৰক"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"এই নেটৱৰ্কটো ব্যৱহাৰ নকৰিব"</string>
- <string name="action_bar_label" msgid="917235635415966620">"নেটৱৰ্কত ছাইন ইন কৰক"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$st ছাইন ইন কৰক"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"আপুনি সংযোগ কৰিবলৈ চেষ্টা কৰি থকা নেটৱৰ্কটোত সুৰক্ষাজনিত সমস্যা আছে।"</string>
- <string name="ssl_error_example" msgid="647898534624078900">"উদাহৰণস্বৰূপে, আপোনাক দেখুওৱা লগ ইনৰ পৃষ্ঠাটো প্ৰতিষ্ঠানটোৰ নিজা নহ\'বও পাৰে।"</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"তথাপি ব্ৰাউজাৰৰ জৰিয়তে অব্যাহত ৰাখক"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"এই প্ৰমাণপত্ৰখন কোনো বিশ্বাসী কর্তৃপক্ষৰ নহয়।"</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"ৱেবচাইটটোৰ নাম আৰু প্ৰমাণপত্ৰখনত থকা নামটোৰ মিল নাই।"</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"এই প্ৰমাণপত্ৰখনৰ ম্যাদ উকলি গৈছে।"</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"এই প্ৰমাণপত্ৰখন এই পর্যন্ত মান্য হোৱা নাই।"</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"এই প্ৰমাণপত্ৰখনত থকা তাৰিখটো মান্য নহয়।"</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"এই প্ৰমাণপত্ৰখন মান্য নহয়।"</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"প্ৰমাণপত্ৰত অজ্ঞাত আসোঁৱাহ।"</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"সুৰক্ষা সম্পর্কীয় সতৰ্কতা"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"প্ৰমাণপত্ৰ চাওক"</string>
- <string name="ok" msgid="2817931639040794018">"ঠিক আছে"</string>
- <string name="page_info_address" msgid="1261481517455692363">"ঠিকনা:"</string>
- <string name="page_info" msgid="4416941086705172545">"পৃষ্ঠাৰ তথ্য"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-az/strings.xml b/packages/CaptivePortalLogin/res/values-az/strings.xml
deleted file mode 100644
index f002544..0000000
--- a/packages/CaptivePortalLogin/res/values-az/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Bu şəbəkəni olduğu kimi istifadə edin"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Bu şəbəkəni istifadə etməyin"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Şəbəkəyə daxil olun"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Daxil olun: %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Qoşulmaq istədiyiniz şəbəkənin təhlükəsizlik problemləri var."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Məsələn, giriş səhifəsi göstərilən təşkilata aid olmaya bilər."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Hər bir halda brazuer ilə davam edin"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Bu sertifikat etibarlı orqan tərəfindən verilməyib."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Saytın adı sertifikatdakı ada uyğun deyil."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Bu sertifikatın müddəti bitib."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Bu sertifikat hələ etibarlı deyil."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Bu sertifikatın tarixi yanlışdır."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Bu sertifikat etibarsızdır."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Bilinməyən sertifikat xətası."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Təhlükəsizlik xəbərdarlığı"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Sertifikata baxın"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Ünvan:"</string>
- <string name="page_info" msgid="4416941086705172545">"Səhifə məlumatı"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-b+sr+Latn/strings.xml b/packages/CaptivePortalLogin/res/values-b+sr+Latn/strings.xml
deleted file mode 100644
index 401548b..0000000
--- a/packages/CaptivePortalLogin/res/values-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Koristi ovu mrežu takvu kakva je"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Ne koristi ovu mrežu"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Prijavi me na mrežu"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Prijavite se u: %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Mreža kojoj pokušavate da se pridružite ima bezbednosnih problema."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Na primer, stranica za prijavljivanje možda ne pripada prikazanoj organizaciji."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Ipak nastavi preko pregledača"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Ovaj sertifikat ne potiče iz pouzdanog izvora."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Naziv sajta se ne podudara sa nazivom na sertifikatu."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Ovaj sertifikat je istekao."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Ovaj sertifikat još uvek nije važeći."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Datum ovog sertifikata je nevažeći."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Ovaj sertifikat je nevažeći."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Nepoznata greška sertifikata."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Bezbednosno upozorenje"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Prikaži sertifikat"</string>
- <string name="ok" msgid="2817931639040794018">"Potvrdi"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adresa:"</string>
- <string name="page_info" msgid="4416941086705172545">"Informacije o stranici"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-be/strings.xml b/packages/CaptivePortalLogin/res/values-be/strings.xml
deleted file mode 100644
index 5f2bd90..0000000
--- a/packages/CaptivePortalLogin/res/values-be/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Выкарыстоўваць гэтую сетку як ёсць"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Не выкарыстоўваць гэту сетку"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Увайсці ў сетку"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Увайсці ў %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"У сеткі, да якой вы спрабуеце далучыцца, ёсць праблемы з бяспекай."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Напрыклад, старонка ўваходу можа не належаць указанай арганізацыі."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Усё роўна працягнуць праз браўзер"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Гэты сертыфікат не выдадзены давераным цэнтрам."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Назва сайта не адпавядае назве ў сертыфікаце."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Тэрмін дзеяння сертыфіката мінуў."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Гэты сертыфікат яшчэ не дзейнічае."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Гэты сертыфікат мае несапраўдную дату."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Гэты сертыфікат несапраўдны."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Невядомая памылка сертыфіката."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Папярэджанне сістэмы бяспекі"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Прагледзець сертыфікат"</string>
- <string name="ok" msgid="2817931639040794018">"ОК"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Адрас:"</string>
- <string name="page_info" msgid="4416941086705172545">"Інфармацыя пра старонку"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-bg/strings.xml b/packages/CaptivePortalLogin/res/values-bg/strings.xml
deleted file mode 100644
index c5354e8..0000000
--- a/packages/CaptivePortalLogin/res/values-bg/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Директно използване на тази мрежа"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Без използване на тази мрежа"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Вход в мрежата"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Влезте в/ъв %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Мрежата, към която опитвате да се присъедините, има проблеми със сигурността."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Например страницата за вход може да не принадлежи на показаната организация."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Продължаване през браузър въпреки това"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Този сертификат не е от надежден орган."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Името на сайта не съответства на името в сертификата."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Този сертификат е изтекъл."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Този сертификат още не е валиден."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Този сертификат е с невалидна дата."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Този сертификат е невалиден."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Неизвестна грешка в сертификата."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Предупреждение относно сигурността"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Преглед на сертификата"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Адрес:"</string>
- <string name="page_info" msgid="4416941086705172545">"Данни за страницата"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-bn/strings.xml b/packages/CaptivePortalLogin/res/values-bn/strings.xml
deleted file mode 100644
index 3fbe2f2..0000000
--- a/packages/CaptivePortalLogin/res/values-bn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"যেভাবে আছে সেভাবেই এই নেটওয়ার্ক ব্যবহার করুন"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"এই নেটওয়ার্ক ব্যবহার করবেন না"</string>
- <string name="action_bar_label" msgid="917235635415966620">"নেটওয়ার্কে সাইন-ইন করুন"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$s তে সাইন-ইন করুন"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"আপনি যে নেটওয়ার্কে যোগ দেওয়ার চেষ্টা করছেন তাতে নিরাপত্তার সমস্যা আছে।"</string>
- <string name="ssl_error_example" msgid="647898534624078900">"উদাহরণস্বরূপ, লগ-ইন পৃষ্ঠাটি প্রদর্শিত প্রতিষ্ঠানের অন্তর্গত নাও হতে পারে৷"</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"যাই হোক না কেন ব্রাউজারের মাধ্যমে অবিরত রাখুন"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"এই সার্টিফিকেটটি একটি বিশ্বস্ত কর্তৃপক্ষের তরফ থেকে নয়।"</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"সাইটের নামটি সার্টিফিকেটে লেখা নামের সাথে মিলছে না।"</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"এই সার্টিফিকেটের মেয়াদ শেষ হয়ে গেছে।"</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"সার্টিফিকেটটি এখনও সঠিক নয়।"</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"এই সার্টিফিকেটের তারিখটি সঠিক নয়।"</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"সার্টিফিকেটটি সঠিক নয়।"</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"সার্টিফিকেটের সমস্যাটি অজানা।"</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"নিরাপত্তার সতর্কতা"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"সার্টিফিকেট দেখুন"</string>
- <string name="ok" msgid="2817931639040794018">"ঠিক আছে"</string>
- <string name="page_info_address" msgid="1261481517455692363">"ঠিকানা:"</string>
- <string name="page_info" msgid="4416941086705172545">"পৃষ্ঠার তথ্য"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ca/strings.xml b/packages/CaptivePortalLogin/res/values-ca/strings.xml
deleted file mode 100644
index 557c3f4..0000000
--- a/packages/CaptivePortalLogin/res/values-ca/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Fes servir aquesta xarxa tal com està."</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"No facis servir aquesta xarxa."</string>
- <string name="action_bar_label" msgid="917235635415966620">"Inicia la sessió a la xarxa"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Inicia la sessió a %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"La xarxa a què et vols connectar té problemes de seguretat."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Per exemple, la pàgina d\'inici de sessió podria no pertànyer a l\'organització que es mostra."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Continua igualment mitjançant el navegador"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Aquest certificat no és d\'una autoritat de confiança."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"El nom del lloc web no coincideix amb el que inclou el certificat."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Aquest certificat ha caducat."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Aquest certificat encara no és vàlid."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Aquest certificat té una data no vàlida."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Aquest certificat no és vàlid."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"S\'ha produït un error desconegut de certificat."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Advertiment de seguretat"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Mostra el certificat"</string>
- <string name="ok" msgid="2817931639040794018">"D\'acord"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adreça:"</string>
- <string name="page_info" msgid="4416941086705172545">"Informació de la pàgina"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-cs/strings.xml b/packages/CaptivePortalLogin/res/values-cs/strings.xml
deleted file mode 100644
index 0328c76..0000000
--- a/packages/CaptivePortalLogin/res/values-cs/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Použít tuto síť tak, jak je"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Tuto síť nepoužívat"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Přihlásit se k síti"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Přihlaste se k síti %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Síť, ke které se pokoušíte připojit, má bezpečnostní problémy."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Například přihlašovací stránka nemusí patřit do zobrazované organizace."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Přesto pokračovat prostřednictvím prohlížeče"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Tento certifikát nepochází od důvěryhodné autority."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Název webu se neshoduje s názvem uvedeným v certifikátu."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Platnost certifikátu vypršela."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Tento certifikát ještě není platný."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Datum tohoto certifikátu není platné."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Tento certifikát je neplatný."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Neznámá chyba certifikátu."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Bezpečnostní upozornění"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Zobrazit certifikát"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adresa:"</string>
- <string name="page_info" msgid="4416941086705172545">"Informace o stránce"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-da/strings.xml b/packages/CaptivePortalLogin/res/values-da/strings.xml
deleted file mode 100644
index b9cf7fe9..0000000
--- a/packages/CaptivePortalLogin/res/values-da/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"Login til captive portal"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Brug dette netværk, som det er"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Brug ikke dette netværk"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Log ind på netværk"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Log ind på %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Der er sikkerhedsproblemer på det netværk, du forsøger at logge ind på."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Det er f.eks. ikke sikkert, at loginsiden tilhører den anførte organisation."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Fortsæt alligevel via browseren"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Dette certifikat stammer ikke fra en pålidelig kilde."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Navnet på websitet stemmer ikke overens med navnet på certifikatet."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Dette certifikat er udløbet."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Dette certifikat er ikke gyldigt endnu."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Dette certifikat har en ugyldig dato."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Dette certifikat er ugyldigt."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Ukendt certifikatfejl."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Sikkerhedsadvarsel"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Se certifikat"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adresse:"</string>
- <string name="page_info" msgid="4416941086705172545">"Sideoplysninger"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-de/strings.xml b/packages/CaptivePortalLogin/res/values-de/strings.xml
deleted file mode 100644
index 4276bf9..0000000
--- a/packages/CaptivePortalLogin/res/values-de/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Netzwerk im Istzustand verwenden"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Netzwerk nicht verwenden"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Im Netzwerk anmelden"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"In %1$s anmelden"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Im Netzwerk, zu dem du eine Verbindung herstellen möchtest, liegen Sicherheitsprobleme vor."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Beispiel: Die Log-in-Seite gehört eventuell nicht zur angezeigten Organisation."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Trotzdem in einem Browser fortfahren"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Dieses Zertifikat stammt nicht von einer vertrauenswürdigen Stelle."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Der Name der Website stimmt nicht mit dem Namen auf dem Zertifikat überein."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Dieses Zertifikat ist abgelaufen."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Dieses Zertifikat ist noch nicht gültig."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Dieses Zertifikat hat ein ungültiges Datum."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Dieses Zertifikat ist ungültig."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Unbekannter Zertifikatfehler."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Sicherheitswarnung"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Zertifikat ansehen"</string>
- <string name="ok" msgid="2817931639040794018">"Ok"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adresse:"</string>
- <string name="page_info" msgid="4416941086705172545">"Seiteninformationen"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-el/strings.xml b/packages/CaptivePortalLogin/res/values-el/strings.xml
deleted file mode 100644
index f4de7c3..0000000
--- a/packages/CaptivePortalLogin/res/values-el/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Χρήση αυτού του δικτύου ως έχει"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Να μη χρησιμοποιείται αυτό το δίκτυο"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Σύνδεση στο δίκτυο"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Συνδεθείτε στο %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Παρουσιάζονται προβλήματα ασφάλειας στο δίκτυο στο οποίο προσπαθείτε να συνδεθείτε."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Για παράδειγμα, η σελίδα σύνδεσης ενδέχεται να μην ανήκει στον οργανισμό που εμφανίζεται."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Συνέχεια ούτως ή άλλως μέσω του προγράμματος περιήγησης"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Αυτό το πιστοποιητικό δεν προέρχεται από αξιόπιστη αρχή."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Το όνομα του ιστοτόπου δεν αντιστοιχεί στο όνομα στο πιστοποιητικό."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Αυτό το πιστοποιητικό έχει λήξει."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Αυτό το πιστοποιητικό δεν είναι έγκυρο ακόμα."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Αυτό το πιστοποιητικό δεν έχει έγκυρη ημερομηνία."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Αυτό το πιστοποιητικό δεν είναι έγκυρο."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Άγνωστο σφάλμα πιστοποιητικού."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Προειδοποίηση ασφαλείας"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Προβολή πιστοποιητικού"</string>
- <string name="ok" msgid="2817931639040794018">"ΟΚ"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Διεύθυνση:"</string>
- <string name="page_info" msgid="4416941086705172545">"Πληροφορίες σελίδας"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-en-rAU/strings.xml b/packages/CaptivePortalLogin/res/values-en-rAU/strings.xml
deleted file mode 100644
index 9763339..0000000
--- a/packages/CaptivePortalLogin/res/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Use this network as is"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Do not use this network"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Sign in to network"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Sign in to %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"The network that you’re trying to join has security issues."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"For example, the login page might not belong to the organisation shown."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"This certificate isn\'t from a trusted authority."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"The name of the site doesn\'t match the name on the certificate."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"This certificate has expired."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"This certificate isn\'t valid yet."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"This certificate has an invalid date."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"This certificate is invalid."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Unknown certificate error."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Security warning"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"View certificate"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Address:"</string>
- <string name="page_info" msgid="4416941086705172545">"Page info"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-en-rCA/strings.xml b/packages/CaptivePortalLogin/res/values-en-rCA/strings.xml
deleted file mode 100644
index 9763339..0000000
--- a/packages/CaptivePortalLogin/res/values-en-rCA/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Use this network as is"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Do not use this network"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Sign in to network"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Sign in to %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"The network that you’re trying to join has security issues."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"For example, the login page might not belong to the organisation shown."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"This certificate isn\'t from a trusted authority."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"The name of the site doesn\'t match the name on the certificate."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"This certificate has expired."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"This certificate isn\'t valid yet."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"This certificate has an invalid date."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"This certificate is invalid."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Unknown certificate error."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Security warning"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"View certificate"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Address:"</string>
- <string name="page_info" msgid="4416941086705172545">"Page info"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml b/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml
deleted file mode 100644
index 9763339..0000000
--- a/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Use this network as is"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Do not use this network"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Sign in to network"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Sign in to %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"The network that you’re trying to join has security issues."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"For example, the login page might not belong to the organisation shown."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"This certificate isn\'t from a trusted authority."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"The name of the site doesn\'t match the name on the certificate."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"This certificate has expired."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"This certificate isn\'t valid yet."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"This certificate has an invalid date."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"This certificate is invalid."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Unknown certificate error."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Security warning"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"View certificate"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Address:"</string>
- <string name="page_info" msgid="4416941086705172545">"Page info"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml b/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml
deleted file mode 100644
index 9763339..0000000
--- a/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Use this network as is"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Do not use this network"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Sign in to network"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Sign in to %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"The network that you’re trying to join has security issues."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"For example, the login page might not belong to the organisation shown."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"This certificate isn\'t from a trusted authority."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"The name of the site doesn\'t match the name on the certificate."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"This certificate has expired."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"This certificate isn\'t valid yet."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"This certificate has an invalid date."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"This certificate is invalid."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Unknown certificate error."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Security warning"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"View certificate"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Address:"</string>
- <string name="page_info" msgid="4416941086705172545">"Page info"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-en-rXC/strings.xml b/packages/CaptivePortalLogin/res/values-en-rXC/strings.xml
deleted file mode 100644
index 8b3ca29..0000000
--- a/packages/CaptivePortalLogin/res/values-en-rXC/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Use this network as is"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Do not use this network"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Sign in to network"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Sign in to %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"The network you’re trying to join has security issues."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"For example, the login page may not belong to the organization shown."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"This certificate isn\'t from a trusted authority."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"The name of the site doesn\'t match the name on the certificate."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"This certificate has expired."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"This certificate isn\'t valid yet."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"This certificate has an invalid date."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"This certificate is invalid."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Unknown certificate error."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Security warning"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"View certificate"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Address:"</string>
- <string name="page_info" msgid="4416941086705172545">"Page info"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml b/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml
deleted file mode 100644
index d7ddaa7..0000000
--- a/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Usar esta red como está"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"No usar esta red"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Acceder a la red"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Acceder a %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"La red a la que intentas conectarte tiene problemas de seguridad."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Por ejemplo, es posible que la página de acceso no pertenezca a la organización que aparece."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Continuar de todos modos desde el navegador"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Este certificado no proviene de una autoridad confiable."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"El nombre del sitio no coincide con el nombre del certificado."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Este certificado venció."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Este certificado aún no es válido."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"La fecha de este certificado no es válida."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Este certificado no es válido."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Error de certificado desconocido"</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Advertencia de seguridad"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Ver certificado"</string>
- <string name="ok" msgid="2817931639040794018">"Aceptar"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Dirección:"</string>
- <string name="page_info" msgid="4416941086705172545">"Información de la página"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-es/strings.xml b/packages/CaptivePortalLogin/res/values-es/strings.xml
deleted file mode 100644
index e10380f..0000000
--- a/packages/CaptivePortalLogin/res/values-es/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Utilizar esta red tal cual"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"No utilizar esta red"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Iniciar sesión en la red"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Inicia sesión en %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"La red a la que intentas unirte tiene problemas de seguridad."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Por ejemplo, es posible que la página de inicio de sesión no pertenezca a la organización mostrada."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Continuar de todos modos a través del navegador"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Este certificado no procede de una entidad de confianza."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"El nombre del sitio web no coincide con el del certificado."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Este certificado ha caducado."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Este certificado aún no es válido."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"La fecha de este certificado no es válida."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Este certificado no es válido."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Error de certificado desconocido."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Advertencia de seguridad"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Ver certificado"</string>
- <string name="ok" msgid="2817931639040794018">"Aceptar"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Dirección:"</string>
- <string name="page_info" msgid="4416941086705172545">"Información de la página"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-et/strings.xml b/packages/CaptivePortalLogin/res/values-et/strings.xml
deleted file mode 100644
index 2894383..0000000
--- a/packages/CaptivePortalLogin/res/values-et/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Kasuta seda võrku olemasoleval kujul"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Ära kasuta seda võrku"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Logi võrku sisse"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Logige sisse: %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Võrgul, millega üritate ühenduse luua, on turvaprobleeme."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Näiteks ei pruugi sisselogimisleht kuuluda kuvatavale organisatsioonile."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Jätka siiski brauseris"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"See sertifikaat ei pärine usaldusväärselt asutuselt."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Saidi nimi ei vasta sertifikaadil olevale nimele."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"See sertifikaat on aegunud."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"See sertifikaat pole veel kehtiv."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Sellel sertifikaadil on kehtetu kuupäev."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"See sertifikaat on kehtetu."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Tundmatu sertifikaadiviga."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Turvahoiatus"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Kuva sertifikaat"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Aadress:"</string>
- <string name="page_info" msgid="4416941086705172545">"Lehe teave"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-eu/strings.xml b/packages/CaptivePortalLogin/res/values-eu/strings.xml
deleted file mode 100644
index b7233ab..0000000
--- a/packages/CaptivePortalLogin/res/values-eu/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Erabili sare hau bere horretan"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Ez erabili sare hau"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Hasi saioa sarean"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Hasi saioa %1$s sarean"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Erabili nahi duzun sareak segurtasun-arazoak ditu."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Adibidez, baliteke saioa hasteko orria adierazitako erakundearena ez izatea."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Jarraitu arakatzailearen bidez, halere"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Ziurtagiria ez da autoritate fidagarri batena."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Webgunearen izena ez dator bat ziurtagirian agertzen den izenarekin."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Ziurtagiria iraungi da."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Ziurtagiriak ez du balio oraindik."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Ziurtagiriaren datak ez du balio."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Ziurtagiriak ez du balio."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Errore ezezagun bat gertatu da ziurtagiriarekin."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Segurtasun-abisua"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Ikusi ziurtagiria"</string>
- <string name="ok" msgid="2817931639040794018">"Ados"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Helbidea:"</string>
- <string name="page_info" msgid="4416941086705172545">"Orriari buruzko informazioa"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-fa/strings.xml b/packages/CaptivePortalLogin/res/values-fa/strings.xml
deleted file mode 100644
index db9f7e8..0000000
--- a/packages/CaptivePortalLogin/res/values-fa/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"از این شبکه همانطور که هست استفاده شود"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"از این شبکه استفاده نشود"</string>
- <string name="action_bar_label" msgid="917235635415966620">"ورود به سیستم شبکه"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"ورود به سیستم %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"شبکهای که میخواهید به آن بپیوندید مشکلات امنیتی دارد."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"به عنوان مثال، صفحه ورود به سیستم ممکن است متعلق به سازمان نشان داده شده نباشد."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"در هر صورت از طریق مرورگر ادامه یابد"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"این گواهی از یک منبع مورداطمینان صادر نشده است."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"نام سایت با نام موجود در گواهی مطابقت ندارد."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"این گواهی منقضی شده است."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"این گواهی هنوز معتبر نیست."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"تاریخ این گواهی نامعتبر است."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"این گواهی نامعتبر است."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"خطای ناشناخته در گواهی."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"اخطار امنیتی"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"مشاهده گواهی"</string>
- <string name="ok" msgid="2817931639040794018">"تأیید"</string>
- <string name="page_info_address" msgid="1261481517455692363">"نشانی:"</string>
- <string name="page_info" msgid="4416941086705172545">"اطلاعات صفحه"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-fi/strings.xml b/packages/CaptivePortalLogin/res/values-fi/strings.xml
deleted file mode 100644
index 1d7fcb0..0000000
--- a/packages/CaptivePortalLogin/res/values-fi/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Käytä tätä verkkoa sellaisenaan"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Älä käytä tätä verkkoa"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Kirjaudu verkkoon"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Kirjaudu sisään kohteeseen %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Verkossa, johon yrität muodostaa yhteyttä, on turvallisuusongelmia."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Kirjautumissivu ei välttämättä kuulu näytetylle organisaatiolle."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Jatka silti selaimen kautta."</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Varmenteen myöntäjä ei ole luotettava taho."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Sivuston nimi ei vastaa varmenteessa olevaa nimeä."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Varmenne ei ole enää voimassa."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Varmenne ei ole vielä voimassa."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Varmenteen päiväys ei kelpaa."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Varmenne on virheellinen."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Tuntematon varmennevirhe."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Suojausvaroitus"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Näytä varmenne"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Osoite:"</string>
- <string name="page_info" msgid="4416941086705172545">"Sivun tiedot"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-fr-rCA/strings.xml b/packages/CaptivePortalLogin/res/values-fr-rCA/strings.xml
deleted file mode 100644
index 979e236..0000000
--- a/packages/CaptivePortalLogin/res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Utiliser ce réseau tel quel"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Ne pas utiliser ce réseau"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Connectez-vous au réseau"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Connexion à %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Le réseau que vous essayez de rejoindre présente des problèmes de sécurité."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Par exemple, la page de connexion pourrait ne pas appartenir à l\'organisation représentée."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Continuer quand même dans un navigateur"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Ce certificat ne provient pas d\'une autorité de confiance."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Le nom du site ne correspond pas au nom figurant sur le certificat."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Ce certificat a expiré."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Ce certificat n\'est pas encore valide."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Ce certificat contient une date non valide."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Ce certificat n\'est pas valide."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Erreur de certificat inconnue."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Avertissement de sécurité"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Afficher le certificat"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adresse :"</string>
- <string name="page_info" msgid="4416941086705172545">"Renseignements sur la page"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-fr/strings.xml b/packages/CaptivePortalLogin/res/values-fr/strings.xml
deleted file mode 100644
index e348a4d..0000000
--- a/packages/CaptivePortalLogin/res/values-fr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Utiliser ce réseau tel quel"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Ne pas utiliser ce réseau"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Se connecter au réseau"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Se connecter à %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Le réseau que vous essayez de rejoindre présente des problèmes de sécurité."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Par exemple, la page de connexion peut ne pas appartenir à l\'organisation représentée."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Continuer quand même dans le navigateur"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Ce certificat provient d\'une autorité non approuvée."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Le nom du site ne correspond pas au nom indiqué dans le certificat."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Ce certificat a expiré."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Ce certificat n\'est pas encore valide."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"La date de ce certificat n\'est pas valide."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Ce certificat n\'est pas valide."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Erreur de certificat inconnue."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Avertissement de sécurité"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Afficher le certificat"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adresse :"</string>
- <string name="page_info" msgid="4416941086705172545">"Informations sur la page"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-gl/strings.xml b/packages/CaptivePortalLogin/res/values-gl/strings.xml
deleted file mode 100644
index 6419516..0000000
--- a/packages/CaptivePortalLogin/res/values-gl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Utilizar esta rede tal como está"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Non utilizar esta rede"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Inicia sesión na rede"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Iniciar sesión en %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"A rede á que tentas unirte ten problemas de seguranza."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Por exemplo, é posible que a páxina de inicio de sesión non pertenza á organización que se mostra."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Continuar igualmente co navegador"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Este certificado non procede dunha autoridade de confianza."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"O nome do sitio non coincide co nome que aparece no certificado."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Este certificado caducou."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Este certificado aínda non é válido."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Este certificado ten unha data non válida."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Este certificado non é válido."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Produciuse un erro descoñecido relacionado co certificado."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Advertencia de seguranza"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Ver certificado"</string>
- <string name="ok" msgid="2817931639040794018">"Aceptar"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Enderezo:"</string>
- <string name="page_info" msgid="4416941086705172545">"Información da páxina"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-gu/strings.xml b/packages/CaptivePortalLogin/res/values-gu/strings.xml
deleted file mode 100644
index aef043f..0000000
--- a/packages/CaptivePortalLogin/res/values-gu/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"આ નેટવર્કનો જેમનો તેમ ઉપયોગ કરો"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"આ નેટવર્કનો ઉપયોગ કરશો નહીં"</string>
- <string name="action_bar_label" msgid="917235635415966620">"નેટવર્ક પર સાઇન ઇન કરો"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$sમાં સઇન ઇન કરો"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"તમે જોડાવાનો પ્રયાસ કરી રહ્યાં છો તે નેટવર્કમાં સુરક્ષા સમસ્યાઓ છે."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"ઉદાહરણ તરીકે, લોગિન પૃષ્ઠ દર્શાવેલ સંસ્થાનું હોઈ શકતું નથી."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"બ્રાઉઝર મારફતે કોઈપણ રીતે ચાલુ રાખો"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"આ પ્રમાણપત્ર વિશ્વસનીય સત્તાધિકારી તરફથી મળ્યું નથી."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"સાઇટનું નામ એ પ્રમાણપત્ર પર રહેલાં નામ સાથે મેળ ખાતું નથી."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"આ પ્રમાણપત્રની સમયસીમા સમાપ્ત થઈ ગઈ છે."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"આ પ્રમાણપત્ર હજુ માન્ય નથી."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"આ પ્રમાણપત્રમાં અમાન્ય તારીખ છે."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"આ પ્રમાણપત્ર અમાન્ય છે."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"પ્રમાણપત્રમાં અજાણી ભૂલ."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"સુરક્ષા વિશે ચેતવણી"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"પ્રમાણપત્ર જુઓ"</string>
- <string name="ok" msgid="2817931639040794018">"ઓકે"</string>
- <string name="page_info_address" msgid="1261481517455692363">"સરનામું:"</string>
- <string name="page_info" msgid="4416941086705172545">"પેજ વિશે માહિતી"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-hi/strings.xml b/packages/CaptivePortalLogin/res/values-hi/strings.xml
deleted file mode 100644
index b8ae82a..0000000
--- a/packages/CaptivePortalLogin/res/values-hi/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"इस नेटवर्क का उपयोग जैसा है वैसा ही करें"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"इस नेटवर्क का उपयोग न करें"</string>
- <string name="action_bar_label" msgid="917235635415966620">"नेटवर्क में साइन इन करें"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$s में साइन इन करें"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"आप जिस नेटवर्क में शामिल होने का प्रयास कर रहे हैं उसमें सुरक्षा समस्याएं हैं."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"उदाहरण के लिए, हो सकता है कि लॉगिन पृष्ठ दिखाए गए संगठन से संबद्ध ना हो."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"ब्राउज़र के द्वारा फिर जारी रखें"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"यह प्रमाणपत्र किसी भरोसेमंद अधिकारी का नहीं है."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"साइट के नाम का मिलान प्रमाणपत्र में दिए नाम से नहीं हो रहा है."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"इस प्रमाणपत्र की समय-सीमा खत्म हो चुकी है."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"यह प्रमाणपत्र अभी तक मान्य नहीं है."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"इस प्रमाणपत्र की तारीख गलत है."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"यह प्रमाणपत्र अमान्य है."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"प्रमाणपत्र में अनजान गड़बड़ी."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"सुरक्षा चेतावनी"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"प्रमाणपत्र देखें"</string>
- <string name="ok" msgid="2817931639040794018">"ठीक है"</string>
- <string name="page_info_address" msgid="1261481517455692363">"पता:"</string>
- <string name="page_info" msgid="4416941086705172545">"पेज की जानकारी"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-hr/strings.xml b/packages/CaptivePortalLogin/res/values-hr/strings.xml
deleted file mode 100644
index f6adcdb..0000000
--- a/packages/CaptivePortalLogin/res/values-hr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Upotrebljavaj ovu mrežu u zatečenom stanju"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Ne upotrebljavaj ovu mrežu"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Prijava na mrežu"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Prijavite se na %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Mreža kojoj se pokušavate pridružiti ima sigurnosne poteškoće."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Na primjer, stranica za prijavu možda ne pripada prikazanoj organizaciji."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Ipak nastavi putem preglednika"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Ovaj certifikat ne potječe iz pouzdanog izvora."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Naziv web-lokacije ne podudara se s nazivom na certifikatu."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Ovaj je certifikat istekao."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Ovaj certifikat još nije važeći."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Ovaj certifikat ima nevažeći datum."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Ovaj certifikat nije važeći."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Nepoznata pogreška certifikata."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Sigurnosno upozorenje"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Pregledajte certifikat"</string>
- <string name="ok" msgid="2817931639040794018">"U redu"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adresa:"</string>
- <string name="page_info" msgid="4416941086705172545">"Informacije o stranici"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-hu/strings.xml b/packages/CaptivePortalLogin/res/values-hu/strings.xml
deleted file mode 100644
index ab289d1..0000000
--- a/packages/CaptivePortalLogin/res/values-hu/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Hálózat használata jelen állapotában"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Ne használja ezt a hálózatot"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Bejelentkezés a hálózatba"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Bejelentkezés a következőbe: %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Biztonsági problémák vannak azzal a hálózattal, amelyhez csatlakozni szeretne."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Például lehet, hogy a bejelentkezési oldal nem a megjelenített szervezethez tartozik."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Folytatás ennek ellenére böngészőn keresztül"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Ez a tanúsítvány nem hiteles tanúsítványkibocsátótól származik."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"A webhely neve nem egyezik a tanúsítványon lévő névvel."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"A tanúsítvány lejárt."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"A tanúsítvány még nem érvényes."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"A tanúsítvány dátuma érvénytelen."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Ez a tanúsítvány érvénytelen."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Ismeretlen tanúsítványhiba."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Biztonsági figyelmeztetés"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Tanúsítvány megtekintése"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Cím:"</string>
- <string name="page_info" msgid="4416941086705172545">"Oldaladatok"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-hy/strings.xml b/packages/CaptivePortalLogin/res/values-hy/strings.xml
deleted file mode 100644
index d859d90..0000000
--- a/packages/CaptivePortalLogin/res/values-hy/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Օգտագործել այս ցանցն ինչպես կա"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Չօգտագործել այս ցանցը"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Մուտք գործել ցանց"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Մուտք գործել %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Ցանցը, որին փորձում եք միանալ, անվտանգության խնդիրներ ունի:"</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Օրինակ՝ մուտքի էջը կարող է ցուցադրված կազմակերպության էջը չլինել:"</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Շարունակել այնուամենայնիվ դիտարկիչի միջոցով"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Հավաստագրի աղբյուրը վստահելի չէ:"</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Կայքի անունը չի համապատասխանում հավաստագրում նշված անվանը:"</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Հավաստագրի ժամկետը լրացել է:"</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Հավաստագիրը դեռ վավեր չէ:"</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Հավաստագրի ամսաթիվն անվավեր է:"</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Հավաստագիրն անվավեր է:"</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Հավաստագրի հետ կապված անհայտ սխալ առաջացավ:"</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Զգուշացում"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Դիտել հավաստագիրը"</string>
- <string name="ok" msgid="2817931639040794018">"Եղավ"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Հասցե՝"</string>
- <string name="page_info" msgid="4416941086705172545">"Էջի մասին"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-in/strings.xml b/packages/CaptivePortalLogin/res/values-in/strings.xml
deleted file mode 100644
index 7036318..0000000
--- a/packages/CaptivePortalLogin/res/values-in/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Gunakan jaringan ini sebagaimana adanya"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Jangan gunakan jaringan ini"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Login ke jaringan"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Login ke %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Jaringan yang ingin Anda masuki mengalami masalah keamanan."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Misalnya, halaman masuk mungkin bukan milik organisasi yang ditampilkan."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Tetap lanjutkan melalui browser"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Sertifikat ini tidak berasal dari otoritas tepercaya."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Nama situs tidak cocok dengan nama pada sertifikat."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Masa berlaku sertifikat ini telah berakhir."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Sertifikat ini belum valid."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Tanggal sertifikat ini tidak valid."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Sertifikat ini tidak valid."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Error sertifikat tak dikenal."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Peringatan keamanan"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Lihat sertifikat"</string>
- <string name="ok" msgid="2817931639040794018">"Oke"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Alamat:"</string>
- <string name="page_info" msgid="4416941086705172545">"Info halaman"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-is/strings.xml b/packages/CaptivePortalLogin/res/values-is/strings.xml
deleted file mode 100644
index 7c2a5e4..0000000
--- a/packages/CaptivePortalLogin/res/values-is/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Nota þetta net óbreytt"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Ekki nota þetta net"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Skrá inn á net"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Skrá inn á %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Öryggisvandamál eru á netinu sem þú ert að reyna að tengjast."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Til dæmis getur verið að innskráningarsíðan tilheyri ekki fyrirtækinu sem birtist."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Halda samt áfram í vafra"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Þetta vottorð er ekki frá traustum aðila."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Nafnið á síðunni passar ekki við nafnið á vottorðinu."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Þetta vottorð er útrunnið."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Þetta vottorð hefur ekki enn tekið gildi."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Þetta vottorð er með ógilda dagsetningu."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Þetta vottorð er ógilt."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Óþekkt vottorðsvilla."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Öryggisviðvörun"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Skoða vottorð"</string>
- <string name="ok" msgid="2817931639040794018">"Í lagi"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Netfang:"</string>
- <string name="page_info" msgid="4416941086705172545">"Upplýsingar um síðu"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-it/strings.xml b/packages/CaptivePortalLogin/res/values-it/strings.xml
deleted file mode 100644
index 3896cb4..0000000
--- a/packages/CaptivePortalLogin/res/values-it/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Utilizza questa rete così com\'è"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Non utilizzare questa rete"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Accedi alla rete"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Accedi a %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"La rete a cui stai tentando di accedere presenta problemi di sicurezza."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Ad esempio, la pagina di accesso potrebbe non appartenere all\'organizzazione indicata."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Continua comunque dal browser"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Questo certificato non proviene da un\'autorità attendibile."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Il nome del sito non corrisponde al nome nel certificato."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Il certificato è scaduto."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Questo certificato non è ancora valido."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Questo certificato presenta una data non valida."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Questo certificato non è valido."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Errore certificato sconosciuto."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Avviso di sicurezza"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Visualizza certificato"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Indirizzo:"</string>
- <string name="page_info" msgid="4416941086705172545">"Informazioni sulla pagina"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-iw/strings.xml b/packages/CaptivePortalLogin/res/values-iw/strings.xml
deleted file mode 100644
index 4ce98ee..0000000
--- a/packages/CaptivePortalLogin/res/values-iw/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"השתמש ברשת זו כפי שהיא"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"אל תשתמש ברשת זו"</string>
- <string name="action_bar_label" msgid="917235635415966620">"היכנס לרשת"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"כניסה אל %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"יש בעיות אבטחה ברשת שאליה אתה מנסה להתחבר."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"לדוגמה, ייתכן שדף ההתחברות אינו שייך לארגון המוצג."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"המשך בכל זאת באמצעות דפדפן"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"אישור זה אינו מגיע מרשות אמינה."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"שם האתר לא תואם לשם באישור."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"פג תוקפו של אישור זה."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"אישור זה אינו חוקי עדיין."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"לאישור זה יש תאריך בלתי חוקי."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"אישור זה אינו חוקי."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"שגיאת אישור לא ידועה."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"אזהרת אבטחה"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"הצגת אישור"</string>
- <string name="ok" msgid="2817931639040794018">"אישור"</string>
- <string name="page_info_address" msgid="1261481517455692363">"כתובת:"</string>
- <string name="page_info" msgid="4416941086705172545">"פרטי דף"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ja/strings.xml b/packages/CaptivePortalLogin/res/values-ja/strings.xml
deleted file mode 100644
index bab9026..0000000
--- a/packages/CaptivePortalLogin/res/values-ja/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"このネットワークをそのまま使用する"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"このネットワークを使用しない"</string>
- <string name="action_bar_label" msgid="917235635415966620">"ネットワークにログイン"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$s にログイン"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"接続しようとしているネットワークにセキュリティの問題があります。"</string>
- <string name="ssl_error_example" msgid="647898534624078900">"たとえば、ログインページが表示されている組織に属していない可能性があります。"</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"ブラウザから続行"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"この証明書は信頼できる認証機関のものではありません。"</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"サイト名と証明書上の名前が一致しません。"</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"この証明書は有効期限切れです。"</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"この証明書はまだ有効ではありません。"</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"この証明書の日付は無効です。"</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"この証明書は無効です。"</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"不明な証明書エラーです。"</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"セキュリティに関する警告"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"証明書を表示"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"メールアドレス:"</string>
- <string name="page_info" msgid="4416941086705172545">"ページ情報"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ka/strings.xml b/packages/CaptivePortalLogin/res/values-ka/strings.xml
deleted file mode 100644
index ec820e5..0000000
--- a/packages/CaptivePortalLogin/res/values-ka/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"ამ ქსელის გამოყენება, როგორც არის"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"ეს ქსელი არ გამოიყენო"</string>
- <string name="action_bar_label" msgid="917235635415966620">"ქსელში შესვლა"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$s-ში შესვლა"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"ქსელს, რომელზედაც მიერთებას ცდილობთ, უსაფრთხოების პრობლემები აქვს."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"მაგალითად, სისტემაში შესვლის გვერდი შეიძლება არ ეკუთვნოდეს ნაჩვენებ ორგანიზაციას."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"ბრაუზერში გაგრძელება"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"ეს სერტიფიკატი არ არის გაცემული ნდობით აღჭურვილი ორგანოს მიერ."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"საიტის სახელი არ ემთხვევა სერტიფიკატში მითითებულს."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"ეს სერტიფიკატი ვადაგასულია."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ეს სერტიფიკატი ჯერ არ არის ძალაში."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"ამ სერტიფიკატში მითითებულია არასწორი თარიღი."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"ეს სერტიფიკატი არასწორია."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"წარმოიშვა სერტიფიკატთან დაკავშირებული უცნობი შეცდომა."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"გაფრთხილება უსაფრთხოების შესახებ"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"სერტიფიკატის ნახვა"</string>
- <string name="ok" msgid="2817931639040794018">"კარგი"</string>
- <string name="page_info_address" msgid="1261481517455692363">"მისამართი:"</string>
- <string name="page_info" msgid="4416941086705172545">"გვერდის ინფორმაცია"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-kk/strings.xml b/packages/CaptivePortalLogin/res/values-kk/strings.xml
deleted file mode 100644
index a23eafd..0000000
--- a/packages/CaptivePortalLogin/res/values-kk/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Осы желіні бар күйінде пайдалану"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Осы желіні пайдаланбау"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Желіге кіру"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$s жүйесіне кіру"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Қосылайын деп жатқан желіңіз қауіпсіз болуы мүмкін."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Мысалы, кіру беті көрсетілген ұйымға тиесілі болмауы мүмкін."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Бәрібір браузер арқылы жалғастыру"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Сертификатты сенімді орган бермеген."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Сайттың атауы мен сертификатта көрсетілген атау сәйкес келмейді."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Сертификаттың жарамдылық мерзімі аяқталған."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Сертификат әлі жарамсыз."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Сертификат күні жарамсыз."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Сертификат жарамсыз."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Сертификатқа қатысты белгісіз қате."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Қауіпсіздік туралы ескерту"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Сертификатты көру"</string>
- <string name="ok" msgid="2817931639040794018">"Жарайды"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Мекенжай:"</string>
- <string name="page_info" msgid="4416941086705172545">"Бет туралы ақпарат"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-km/strings.xml b/packages/CaptivePortalLogin/res/values-km/strings.xml
deleted file mode 100644
index b4274e9..0000000
--- a/packages/CaptivePortalLogin/res/values-km/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"ប្រើបណ្ដាញនេះជា"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"កុំប្រើបណ្ដាញនេះ"</string>
- <string name="action_bar_label" msgid="917235635415966620">"ចូលទៅបណ្ដាញ"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"ចូលទៅ %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"បណ្តាញដែលអ្នកកំពុងព្យាយាមចូលមានបញ្ហាសុវត្ថិភាព។"</string>
- <string name="ssl_error_example" msgid="647898534624078900">"ឧបករណ៍៖ ទំព័រចូលនេះអាចនឹងមិនមែនជាកម្មសិទ្ធិរបស់ស្ថាប័នដែលបានបង្ហាញនេះទេ។"</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"យ៉ាងណាក៏ដោយនៅតែបន្តតាមរយៈកម្មវិធីរុករក"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"វិញ្ញាបនបត្រនេះមិនបានចេញដោយអាជ្ញាធរដែលគួរឱ្យទុកចិត្តទេ។"</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"ឈ្មោះគេហទំព័រនេះមិនត្រូវនឹងឈ្មោះនៅលើវិញ្ញាបនបត្រទេ។"</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"វិញ្ញាបនបត្រនេះផុតកំណត់ហើយ។"</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"វិញ្ញាបនបត្រនេះមិនទាន់មានសុពលភាពនៅឡើយទេ។"</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"វិញ្ញាបនបត្រនេះមានកាលបរិច្ឆេទដែលគ្មានសុពលភាព។"</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"វិញ្ញាបនបត្រនេះគ្មានសុពលភាពទេ។"</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"មានបញ្ហាដោយសារមិនស្គាល់វិញ្ញាបនបត្រ។"</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"ការព្រមានផ្នែកសុវត្ថិភាព"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"មើលវិញ្ញាបនបត្រ"</string>
- <string name="ok" msgid="2817931639040794018">"យល់ព្រម"</string>
- <string name="page_info_address" msgid="1261481517455692363">"អាសយដ្ឋាន៖"</string>
- <string name="page_info" msgid="4416941086705172545">"ព័ត៌មានទំព័រ"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-kn/strings.xml b/packages/CaptivePortalLogin/res/values-kn/strings.xml
deleted file mode 100644
index 731bbcc..0000000
--- a/packages/CaptivePortalLogin/res/values-kn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"ಈ ನೆಟ್ವರ್ಕ್ ಅನ್ನು ಹೀಗೆ ಬಳಸಿ"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"ಈ ನೆಟ್ವರ್ಕ್ ಬಳಸಬೇಡಿ"</string>
- <string name="action_bar_label" msgid="917235635415966620">"ನೆಟ್ವರ್ಕ್ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$s ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"ನೀವು ಸೇರಬೇಕೆಂದಿರುವ ನೆಟ್ವರ್ಕ್ ಭದ್ರತೆ ಸಮಸ್ಯೆಗಳನ್ನು ಹೊಂದಿದೆ."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"ಉದಾಹರಣೆಗೆ, ಲಾಗಿನ್ ಪುಟವು ತೋರಿಸಲಾಗಿರುವ ಸಂಸ್ಥೆಗೆ ಸಂಬಂಧಿಸಿರುವುದಿಲ್ಲ."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"ಹೇಗಾದರೂ ಬ್ರೌಸರ್ ಮೂಲಕ ಮುಂದುವರಿಸಿ"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"ಈ ಪ್ರಮಾಣಪತ್ರವು ವಿಶ್ವಾಸಾರ್ಹ ಪ್ರಾಧಿಕಾರದಿಂದ ಬಂದಿಲ್ಲ."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"ಸೈಟ್ನ ಹೆಸರು ಪ್ರಮಾಣಪತ್ರದಲ್ಲಿನ ಹೆಸರಿನ ಜೊತೆಗೆ ಹೊಂದಾಣಿಕೆಯಾಗುತ್ತಿಲ್ಲ."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"ಈ ಪ್ರಮಾಣಪತ್ರದ ಅವಧಿ ಮೀರಿದೆ."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ಈ ಪ್ರಮಾಣಪತ್ರವು ಇನ್ನೂ ಮಾನ್ಯವಾಗಿಲ್ಲ."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"ಈ ಪ್ರಮಾಣಪತ್ರವು ಅಮಾನ್ಯವಾದ ದಿನಾಂಕವನ್ನು ಹೊಂದಿದೆ."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"ಈ ಪ್ರಮಾಣಪತ್ರವು ಅಮಾನ್ಯವಾಗಿದೆ."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"ಪ್ರಮಾಣಪತ್ರದ ಅಜ್ಞಾತ ದೋಷ."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"ಭದ್ರತಾ ಎಚ್ಚರಿಕೆ"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"ಪ್ರಮಾಣಪತ್ರವನ್ನು ವೀಕ್ಷಿಸಿ"</string>
- <string name="ok" msgid="2817931639040794018">"ಸರಿ"</string>
- <string name="page_info_address" msgid="1261481517455692363">"ವಿಳಾಸ:"</string>
- <string name="page_info" msgid="4416941086705172545">"ಪುಟದ ಮಾಹಿತಿ"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ko/strings.xml b/packages/CaptivePortalLogin/res/values-ko/strings.xml
deleted file mode 100644
index 8c2d9d2..0000000
--- a/packages/CaptivePortalLogin/res/values-ko/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"현재 상태로 이 네트워크 사용"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"이 네트워크 사용 안함"</string>
- <string name="action_bar_label" msgid="917235635415966620">"네트워크에 로그인"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$s에 로그인"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"가입하려는 네트워크에 보안 문제가 있습니다."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"예를 들어 로그인 페이지가 표시된 조직에 속하지 않을 수 있습니다."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"브라우저를 통해 계속하기"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"신뢰할 수 있는 인증 기관에서 발급한 인증서가 아닙니다."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"사이트 이름이 인증서상의 이름과 일치하지 않습니다."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"인증서가 만료되었습니다."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"인증서가 아직 유효하지 않습니다."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"인증서의 날짜가 잘못되었습니다."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"인증서가 잘못되었습니다."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"알 수 없는 인증서 오류입니다."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"보안 경고"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"인증서 보기"</string>
- <string name="ok" msgid="2817931639040794018">"확인"</string>
- <string name="page_info_address" msgid="1261481517455692363">"주소:"</string>
- <string name="page_info" msgid="4416941086705172545">"페이지 정보"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ky/strings.xml b/packages/CaptivePortalLogin/res/values-ky/strings.xml
deleted file mode 100644
index 6d663cb..0000000
--- a/packages/CaptivePortalLogin/res/values-ky/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Бул тармак кандай болсо, ошондой колдонулсун"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Бул тармак колдонулбасын"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Тармакка кирүү"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$s каттоо эсебине кириңиз"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Кошулайын деген тармагыңызда коопсуздук көйгөйлөрү бар."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Мисалы, каттоо эсебине кирүү баракчасы көрсөтүлгөн уюмга таандык эмес болушу мүмкүн."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Баары бир серепчи аркылуу улантуу"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Бул тастыктама ишеничтүү мекеме аркылуу берилген эмес."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Сайттын аталышы тастыктаманын аталышына дал келбейт."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Бул тастыктаманын мөөнөтү өтүп кеткен."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Бул тастыктама азырынча жараксыз."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Бул тастыктаманын күнү жараксыз."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Бул тастыктама жараксыз."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Тастыктамага байланыштуу белгисиз ката."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Коопсуздук эскертүүсү"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Тастыктаманы көрүү"</string>
- <string name="ok" msgid="2817931639040794018">"ЖАРАЙТ"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Дарек:"</string>
- <string name="page_info" msgid="4416941086705172545">"Барак жөнүндө маалымат"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-lo/strings.xml b/packages/CaptivePortalLogin/res/values-lo/strings.xml
deleted file mode 100644
index 673bf2c..0000000
--- a/packages/CaptivePortalLogin/res/values-lo/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"ໃຊ້ເຄືອຂ່າຍນີ້ຕາມທີ່ມັນເປັນ"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"ບໍ່ໃຊ້ເຄືອຂ່າຍນີ້"</string>
- <string name="action_bar_label" msgid="917235635415966620">"ລົງຊື່ເຂົ້າເຄືອຂ່າຍ"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"ເຂົ້າສູ່ລະບົບ %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"ເຄືອຂ່າຍທີ່ທ່ານກຳລັງເຂົ້າຮ່ວມມີບັນຫາຄວາມປອດໄພ."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"ຕົວຢ່າງ, ໜ້າລົງຊື່ເຂົ້າໃຊ້ອາດຈະບໍ່ເປັນຂອງອົງການທີ່ສະແດງຂຶ້ນ."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"ແນວໃດກໍ່ສືບຕໍ່ຜ່ານບຣາວເຊີ"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"ໃບຮັບຮອງນີ້ບໍ່ໄດ້ມາຈາກໜ່ວຍງານທີ່ເຊື່ອຖືໄດ້."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"ຊື່ຂອງເວັບໄຊບໍ່ກົງກັບຊື່ຢູ່ໃບຮັບຮອງ."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"ໃບຮັບຮອງນີ້ຫມົດອາຍຸແລ້ວ."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ໃບຮັບຮອງນີ້ບໍ່ຖືກຕ້ອງເທື່ອ."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"ໃບຮັບຮອງນີ້ມີວັນທີບໍ່ຖືກຕ້ອງ."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"ໃບຮັບຮອງນີ້ບໍ່ຖືກຕ້ອງ."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"ໃບຮັບຮອງມີຄວາມຜິດພາດທີ່ບໍ່ຮູ້ຈັກ."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"ຄຳເຕືອນຄວາມປອດໄພ"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"ເບິ່ງໃບຮັບຮອງ"</string>
- <string name="ok" msgid="2817931639040794018">"ຕົກລົງ"</string>
- <string name="page_info_address" msgid="1261481517455692363">"ທີ່ຢູ່:"</string>
- <string name="page_info" msgid="4416941086705172545">"ຂໍ້ມູນໜ້າ"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-lt/strings.xml b/packages/CaptivePortalLogin/res/values-lt/strings.xml
deleted file mode 100644
index eb4c46c..0000000
--- a/packages/CaptivePortalLogin/res/values-lt/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Naudoti šį tinklą tokį, koks yra"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Nenaudoti šio tinklo"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Prisijungti prie tinklo"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Prisijungimas prie „%1$s“"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Kilo tinklo, prie kurio bandote prisijungti, problemų."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Pavyzdžiui, prisijungimo puslapis gali nepriklausyti rodomai organizacijai."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Vis tiek tęsti naudojant naršyklę"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Šį sertifikatą išdavė nepatikima įstaiga."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Svetainės pavadinimas neatitinka sertifikate nurodyto pavadinimo."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Šio sertifikato galiojimo laikas baigėsi."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Šis sertifikatas dar negalioja."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Šio sertifikato data netinkama."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Šis sertifikatas netinkamas."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Nežinoma sertifikato klaida."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Saugos įspėjimas"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Žiūrėti sertifikatą"</string>
- <string name="ok" msgid="2817931639040794018">"Gerai"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adresas"</string>
- <string name="page_info" msgid="4416941086705172545">"Puslapio informacija"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-lv/strings.xml b/packages/CaptivePortalLogin/res/values-lv/strings.xml
deleted file mode 100644
index 5554343..0000000
--- a/packages/CaptivePortalLogin/res/values-lv/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Izmantot tīklu ar pašreizējiem iestatījumiem"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Neizmantot šo tīklu"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Pierakstīties tīklā"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Pierakstieties produktā %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Tīklam, kuram mēģināt pievienoties, ir drošības problēmas."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Piemēram, pieteikšanās lapa, iespējams, nepieder norādītajai organizācijai."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Tik un tā turpināt, izmantojot pārlūkprogrammu"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Šo sertifikātu nav izsniegusi uzticama iestāde."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Vietnes nosaukums neatbilst nosaukumam sertifikātā."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Šī sertifikāta derīguma termiņš ir beidzies."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Šis sertifikāts vēl nav derīgs."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Šī sertifikāta datums nav derīgs."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Šis sertifikāts nav derīgs."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Nezināma sertifikāta kļūda."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Drošības brīdinājums"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Skatīt sertifikātu"</string>
- <string name="ok" msgid="2817931639040794018">"Labi"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adrese:"</string>
- <string name="page_info" msgid="4416941086705172545">"Lapas informācija"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-mk/strings.xml b/packages/CaptivePortalLogin/res/values-mk/strings.xml
deleted file mode 100644
index 9a57fca..0000000
--- a/packages/CaptivePortalLogin/res/values-mk/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Користи ја мрежата во оваа состојба"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Не ја користи мрежата"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Најавете се на мрежа"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Најавете се на %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Мрежата на која се обидувате да се придружите има проблеми со безбедноста."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"На пример, страницата за најавување може да не припаѓа на организацијата што е прикажана."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Сепак продолжи преку прелистувач"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Сертификатов не е од доверлив орган."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Името на сајтот не се совпаѓа со името на сертификатот."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Сертификатов е истечен."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Сертификатов сѐ уште не е важечки."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Сертификатов има неважечки датум."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Сертификатов е неважечки."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Непозната грешка на сертификатот."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Предупредување за безбедност"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Прикажи го сертификатот"</string>
- <string name="ok" msgid="2817931639040794018">"Во ред"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Адреса:"</string>
- <string name="page_info" msgid="4416941086705172545">"Информации за страницата"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ml/strings.xml b/packages/CaptivePortalLogin/res/values-ml/strings.xml
deleted file mode 100644
index 8fd74d1..0000000
--- a/packages/CaptivePortalLogin/res/values-ml/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"ഈ നെറ്റ്വർക്ക് മാറ്റമൊന്നും വരുത്താതെ ഉപയോഗിക്കുക"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"ഈ നെറ്റ്വർക്ക് ഉപയോഗിക്കരുത്"</string>
- <string name="action_bar_label" msgid="917235635415966620">"നെറ്റ്വർക്കിൽ സൈൻ ഇൻ ചെയ്യുക"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$s എന്നതിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"നിങ്ങൾ ചേരാൻ ശ്രമിക്കുന്ന നെറ്റ്വർക്കിൽ സുരക്ഷാ പ്രശ്നങ്ങളുണ്ടായിരിക്കാം."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"ഉദാഹരണത്തിന്, കാണിച്ചിരിക്കുന്ന ഓർഗനൈസേഷന്റേതായിരിക്കില്ല ലോഗിൻ പേജ്."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"എന്തായാലും ബ്രൗസർ വഴി തുടരുക"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"ഈ സർട്ടിഫിക്കറ്റ്, വിശ്വസനീയ അതോറിറ്റിയിൽ നിന്നുള്ളതല്ല."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"സൈറ്റിന്റെ പേര്, സർട്ടിഫിക്കറ്റിലുള്ള പേരുമായി പൊരുത്തപ്പെടുന്നില്ല."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"ഈ സർട്ടിഫിക്കറ്റ് കാലഹരണപ്പെട്ടു."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ഈ സർട്ടിഫിക്കറ്റിന് ഇപ്പോഴും സാധുതയില്ല."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"ഈ സർട്ടിഫിക്കറ്റിൽ അസാധുവായ തീയതിയാണുള്ളത്."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"ഈ സർട്ടിഫിക്കറ്റ് അസാധുവാണ്."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"അജ്ഞാത സർട്ടിഫിക്കറ്റ് പിശക്."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"സുരക്ഷാ മുന്നറിയിപ്പ്"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"സർട്ടിഫിക്കറ്റ് കാണുക"</string>
- <string name="ok" msgid="2817931639040794018">"ശരി"</string>
- <string name="page_info_address" msgid="1261481517455692363">"വിലാസം:"</string>
- <string name="page_info" msgid="4416941086705172545">"പേജ് വിവരം"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-mn/strings.xml b/packages/CaptivePortalLogin/res/values-mn/strings.xml
deleted file mode 100644
index f11f1d6..0000000
--- a/packages/CaptivePortalLogin/res/values-mn/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Энэ сүлжээг ашиглана уу"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Энэ сүлжээг бүү ашиглана уу"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Сүлжээнд нэвтэрнэ үү"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$s-д нэвтрэх"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Таны нэгдэх гэж буй сүлжээ аюулгүй байдлын асуудалтай байна."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Жишээлбэл нэвтрэх хуудас нь харагдах байгууллагынх биш байж болзошгүй."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Ямартаа ч хөтчөөр үргэлжлүүлэх"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Энэ сертификатыг итгэмжлэгдсэн байгууллагаас олгоогүй байна."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Сайтын нэр сертификат дээрх нэртэй таарахгүй байна."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Энэ сертификатын хугацаа дууссан байна."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Энэ сертификат одоогоор хүчингүй байна."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Энэ сертификатын огноо хүчингүй байна."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Энэ сертификат хүчингүй байна."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Сертификатын үл мэдэгдэх алдаа гарлаа."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Аюулгүй байдлын анхааруулга"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Сертификатыг харах"</string>
- <string name="ok" msgid="2817931639040794018">"ЗА"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Хаяг:"</string>
- <string name="page_info" msgid="4416941086705172545">"Хуудасны мэдээлэл"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-mr/strings.xml b/packages/CaptivePortalLogin/res/values-mr/strings.xml
deleted file mode 100644
index 0adc92e..0000000
--- a/packages/CaptivePortalLogin/res/values-mr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"हे नेटवर्क जसेच्या तसे वापरा"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"हे नेटवर्क वापरू नका"</string>
- <string name="action_bar_label" msgid="917235635415966620">"नेटवर्क मध्ये साइन इन करा"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$sमध्ये साइन इन करा"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"ज्या नेटवर्कमध्ये तुम्ही सामील होण्याचा प्रयत्न करीत आहात त्यात सुरक्षितता समस्या आहेत."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"उदाहरणार्थ, लॉगिन पृष्ठ कदाचित दर्शविलेल्या संस्थेच्या मालकीचे नसावे."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"ब्राउझरद्वारे तरीही सुरु ठेवा"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"हे प्रमाणपत्र विश्वसनीय संस्थेने दिलेले नाही"</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"साइटचे नाव प्रमाणपत्रावरील नावाशी जुळत नाही."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"हे प्रमाणपत्र एक्स्पायर झाले आहे."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"हे प्रमाणपत्र अजून योग्य नाही."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"या प्रमाणपत्राची तारीख चुकीची आहे."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"हे प्रमाणपत्र चुकीचे आहे."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"अज्ञात प्रमाणपत्र एरर."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"सुरक्षितता चेतावणी"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"प्रमाणपत्र पाहा"</string>
- <string name="ok" msgid="2817931639040794018">"ठीक आहे"</string>
- <string name="page_info_address" msgid="1261481517455692363">"पत्ता:"</string>
- <string name="page_info" msgid="4416941086705172545">"पेज माहिती"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ms/strings.xml b/packages/CaptivePortalLogin/res/values-ms/strings.xml
deleted file mode 100644
index cc4957e..0000000
--- a/packages/CaptivePortalLogin/res/values-ms/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Gunakan rangkaian ini"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Jangan gunakan rangkaian ini"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Log masuk ke rangkaian"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Log masuk ke %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Rangkaian yang anda cuba sertai mempunyai isu keselamatan."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Contohnya, halaman log masuk mungkin bukan milik organisasi yang ditunjukkan."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Teruskan juga melalui penyemak imbas"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Sijil ini bukan daripada pihak berkuasa yang dipercayai."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Nama tapak tidak sepadan dengan nama pada sijil."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Sijil ini telah tamat tempoh."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Sijil ini belum sah."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Sijil ini mempunyai tarikh yang tidak sah."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Sijil ini tidak sah."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Ralat sijil tidak diketahui."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Amaran keselamatan"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Lihat sijil"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Alamat:"</string>
- <string name="page_info" msgid="4416941086705172545">"Maklumat halaman"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-my/strings.xml b/packages/CaptivePortalLogin/res/values-my/strings.xml
deleted file mode 100644
index 04a07a4..0000000
--- a/packages/CaptivePortalLogin/res/values-my/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"ဒီကွန်ရက်ကို လက်ရှိအတိုင်း သုံးရန်"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"ဒီကွန်ရက်ကို မသုံးပါနှင့်"</string>
- <string name="action_bar_label" msgid="917235635415966620">"ကွန်ယက်သို့ လက်မှတ်ထိုးဝင်ရန်"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$s သို့ လက်မှတ်ထိုးဝင်ပါ"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"သင်ချိတ်ဆက်ရန် ကြိုးစားနေသည့် ကွန်ရက်သည် လုံခြုံရေးပြဿနာ ရှိနေသည်။"</string>
- <string name="ssl_error_example" msgid="647898534624078900">"ဥပမာ၊ ဝင်ရောက်ရန် စာမျက်နှာသည် ပြသထားသည့် အဖွဲ့အစည်းနှင့် သက်ဆိုင်မှု မရှိနိုင်ပါ။"</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"ဘရောက်ဇာမှတစ်ဆင့် ဆက်လုပ်ရန်"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"ဤအသိအမှတ်ပြုလက်မှတ်သည် ယုံကြည်ရသော စစ်ဆေးရေးအဖွဲ့ထံမှ မဟုတ်ပါ။"</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"ဝဘ်ဆိုက်အမည်သည် အသိအမှတ်ပြုလက်မှတ်ရှိ အမည်နှင့် မကိုက်ညီပါ။"</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"ဤအသိအမှတ်ပြုလက်မှတ် သက်တမ်းကုန်နေပါပြီ။"</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ဤအသိအမှတ်ပြုလက်မှတ်သည် မမှန်ကန်သေးပါ။"</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"ဤအသိအမှတ်ပြုလက်မှတ်ရှိ ရက်စွဲ မမှန်ကန်ပါ။"</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"ဤအသိအမှတ်ပြုလက်မှတ် မမှန်ကန်ပါ။"</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"အသိအမှတ်ပြုလက်မှတ်ဆိုင်ရာ အမျိုးအမည်မသိ အမှား။"</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"လုံခြုံရေး သတိပေးချက်"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"အသိအမှတ်ပြုလက်မှတ်ကို ကြည့်ရန်"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"လိပ်စာ-"</string>
- <string name="page_info" msgid="4416941086705172545">"စာမျက်နှာ အချက်အလက်"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-nb/strings.xml b/packages/CaptivePortalLogin/res/values-nb/strings.xml
deleted file mode 100644
index 29ab438..0000000
--- a/packages/CaptivePortalLogin/res/values-nb/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Bruk dette nettverket som det er"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Ikke bruk dette nettverket"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Logg på nettverk"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Logg på %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Nettverket du prøver å logge på, har sikkerhetsproblemer."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Det er for eksempel mulig at påloggingssiden kanskje ikke tilhører organisasjonen som vises."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Fortsett likevel via nettleseren"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Sertifikatet er ikke fra en pålitelig myndighet."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Navnet på nettstedet samsvarer ikke med navnet på sertifikatet."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Sertifikatet er utløpt."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Sertifikatet er ikke gyldig ennå."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Dette sertifikatet har en ugyldig dato."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Dette sertifikatet er ugyldig."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Ukjent sertifikatfeil."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Sikkerhetsadvarsel"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Vis sertifikatet"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adresse:"</string>
- <string name="page_info" msgid="4416941086705172545">"Sideinformasjon"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ne/strings.xml b/packages/CaptivePortalLogin/res/values-ne/strings.xml
deleted file mode 100644
index 07d7231..0000000
--- a/packages/CaptivePortalLogin/res/values-ne/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"यो सञ्जाल जस्तो छ प्रयोग गर्नुहोस्"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"यो सञ्जाल प्रयोग नगर्नुहोस्"</string>
- <string name="action_bar_label" msgid="917235635415966620">"सञ्जालमा साइन इन गर्नुहोस्"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$s मा साइन इन गर्नुहोस्"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"तपाईँले सामेल हुन प्रयास गरिरहनु भएको नेटवर्कमा सुरक्षा मुद्दाहरू छन्।"</string>
- <string name="ssl_error_example" msgid="647898534624078900">"उदाहरणका लागि, लग इन पृष्ठ देखाइएको संस्थाको नहुन सक्छ।"</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"जे भए पनि ब्राउजर मार्फत जारी राख्नुहोस्"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"यो विश्वसनीय प्राधिकरणबाट उपलब्ध गराइएको प्रमाणपत्र होइन।"</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"प्रमाणपत्रमा भएको नाम साइटमा भएको नामसँग मेल खाँदैन।"</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"यो प्रमाणपत्रको म्याद समाप्त भयो।"</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"यो प्रमाणपत्र अझै मान्य छैन।"</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"यो प्रमाणपत्रमा कुनै अमान्य मिति छ।"</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"यो प्रमाणपत्र अमान्य छ।"</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"प्रमाणपत्रसम्बन्धी अज्ञात त्रुटि।"</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"सुरक्षासम्बन्धी चेतावनी"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"प्रमाणपत्र हेर्नुहोस्"</string>
- <string name="ok" msgid="2817931639040794018">"ठिक छ"</string>
- <string name="page_info_address" msgid="1261481517455692363">"ठेगाना:"</string>
- <string name="page_info" msgid="4416941086705172545">"पृष्ठको जानकारी"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-nl/strings.xml b/packages/CaptivePortalLogin/res/values-nl/strings.xml
deleted file mode 100644
index a4df7b7..0000000
--- a/packages/CaptivePortalLogin/res/values-nl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Dit netwerk in de huidige staat gebruiken"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Dit netwerk niet gebruiken"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Inloggen bij netwerk"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Inloggen bij %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Het netwerk waarmee u verbinding probeert te maken, heeft beveiligingsproblemen."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Zo hoort de weergegeven inlogpagina misschien niet bij de weergegeven organisatie."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Toch doorgaan via browser"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Dit is geen certificaat van een vertrouwde autoriteit."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"De naam van deze site komt niet overeen met de naam op het certificaat."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Dit certificaat is verlopen."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Dit certificaat is nog niet geldig."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Dit certificaat heeft een ongeldige datum."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Dit certificaat is ongeldig."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Onbekende certificaatfout."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Beveiligingswaarschuwing"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Certificaat weergeven"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adres:"</string>
- <string name="page_info" msgid="4416941086705172545">"Paginagegevens"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-or/strings.xml b/packages/CaptivePortalLogin/res/values-or/strings.xml
deleted file mode 100644
index f4c5dac..0000000
--- a/packages/CaptivePortalLogin/res/values-or/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"ଏହି ନେଟ୍ୱର୍କ ଯେପରି ଅଛି, ସେହିପରି ବ୍ୟବହାର କରନ୍ତୁ"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"ଏହି ନେଟ୍ୱର୍କକୁ ବ୍ୟବହାର କରନ୍ତୁ ନାହିଁ"</string>
- <string name="action_bar_label" msgid="917235635415966620">"ନେଟ୍ୱର୍କରେ ସାଇନ୍ ଇନ୍ କରନ୍ତୁ"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$sରେ ସାଇନ୍-ଇନ୍ କରନ୍ତୁ"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"ଆପଣ ଯୋଗ ଦେବାକୁ ଚେଷ୍ଟା କରୁଥିବା ନେଟ୍ୱର୍କର ସୁରକ୍ଷା ସମସ୍ୟା ଅଛି।"</string>
- <string name="ssl_error_example" msgid="647898534624078900">"ଉଦାହରଣସ୍ୱରୂପ, ଲଗଇନ୍ ପୃଷ୍ଠା ଦେଖାଯାଇଥିବା ସଂସ୍ଥାର ନହୋଇଥାଇପାରେ।"</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"ବ୍ରାଉଜର୍ ଜରିଆରେ ଯେମିତିବି ହେଉ ଜାରି ରଖନ୍ତୁ"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"ଏହି ସାର୍ଟିଫିକେଟ୍ ଏକ ବିଶ୍ବସ୍ତ କର୍ତ୍ତୃପକ୍ଷଙ୍କ ଠାରୁ ନୁହେଁ।"</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"ସାଇଟ୍ର ନାମ ସାର୍ଟିଫିକେଟ୍ର ନାମ ସହ ମିଶୁ ନାହିଁ।"</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"ଏହି ସାର୍ଟିଫିକେଟ୍ ସମୟସୀମା ଶେଷ ହୋଇଛି।"</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ଏହି ସାର୍ଟିଫିକେଟ୍ ବର୍ତ୍ତମାନ ପର୍ଯ୍ୟନ୍ତ ବୈଧ ନୁହେଁ।"</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"ଏହି ସାର୍ଟିଫିକେଟ୍ରେ ଏକ ଅବୈଧ ତାରିଖ ରହିଛି।"</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"ଏହି ସାର୍ଟିଫିକେଟ୍ ବୈଧ ନୁହେଁ।"</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"ଅଜଣା ସାର୍ଟିଫିକେଟ୍ ତ୍ରୁଟି।"</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"ସୁରକ୍ଷା ଚେତାବନୀ"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"ସାର୍ଟିଫିକେଟ୍ ଦେଖନ୍ତୁ"</string>
- <string name="ok" msgid="2817931639040794018">"ଠିକ୍ ଅଛି"</string>
- <string name="page_info_address" msgid="1261481517455692363">"ଠିକଣା:"</string>
- <string name="page_info" msgid="4416941086705172545">"ପୃଷ୍ଠା ସୂଚନା"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-pa/strings.xml b/packages/CaptivePortalLogin/res/values-pa/strings.xml
deleted file mode 100644
index b0d6187..0000000
--- a/packages/CaptivePortalLogin/res/values-pa/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"ਇਸ ਨੈੱਟਵਰਕ ਨੂੰ ਉਵੇਂ ਵਰਤੋ ਜਿਵੇਂ ਇਹ ਹੈ"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"ਇਹ ਨੈੱਟਵਰਕ ਨਾ ਵਰਤੋ"</string>
- <string name="action_bar_label" msgid="917235635415966620">"ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$s \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"ਤੁਹਾਡੇ ਦੁਆਰਾ ਸ਼ਾਮਿਲ ਹੋਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੇ ਜਾ ਰਹੇ ਨੈੱਟਵਰਕ ਵਿੱਚ ਸੁਰੱਖਿਆ ਸੰਬੰਧੀ ਸਮੱਸਿਆਵਾਂ ਹਨ।"</string>
- <string name="ssl_error_example" msgid="647898534624078900">"ਉਦਾਹਰਣ ਵੱਜੋਂ, ਲੌਗ-ਇਨ ਪੰਨਾ ਦਿਖਾਈ ਗਈ ਸੰਸਥਾ ਨਾਲ ਸੰਬੰਧਿਤ ਨਹੀਂ ਹੋ ਸਕਦਾ ਹੈ।"</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"ਬ੍ਰਾਊਜ਼ਰ ਰਾਹੀਂ ਫਿਰ ਵੀ ਜਾਰੀ ਰੱਖੋ"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"ਇਹ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਕਿਸੇ ਭਰੋਸੇਯੋਗ ਅਥਾਰਿਟੀ ਦਾ ਨਹੀਂ ਹੈ।"</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"ਸਾਈਟ ਦਾ ਨਾਮ ਪ੍ਰਮਾਣ-ਪੱਤਰ \'ਤੇ ਦਿੱਤੇ ਨਾਮ ਨਾਲ ਮੇਲ ਨਹੀਂ ਖਾਂਦਾ ਹੈ।"</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"ਇਸ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਦੀ ਮਿਆਦ ਸਮਾਪਤ ਹੋ ਗਈ ਹੈ।"</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ਇਹ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਹਾਲੇ ਵੈਧ ਨਹੀਂ ਹੈ।"</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"ਇਸ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਦੀ ਤਾਰੀਖ ਅਵੈਧ ਹੈ।"</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"ਇਹ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਅਵੈਧ ਹੈ।"</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"ਅਗਿਆਤ ਪ੍ਰਮਾਣ-ਪੱਤਰ ਗੜਬੜ।"</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"ਸੁਰੱਖਿਆ ਚਿਤਾਵਨੀ"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"ਪ੍ਰਮਾਣ-ਪੱਤਰ ਦੇਖੋ"</string>
- <string name="ok" msgid="2817931639040794018">"ਠੀਕ ਹੈ"</string>
- <string name="page_info_address" msgid="1261481517455692363">"ਪਤਾ:"</string>
- <string name="page_info" msgid="4416941086705172545">"ਪੰਨੇ ਦੀ ਜਾਣਕਾਰੀ"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-pl/strings.xml b/packages/CaptivePortalLogin/res/values-pl/strings.xml
deleted file mode 100644
index 178734d..0000000
--- a/packages/CaptivePortalLogin/res/values-pl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Używaj tej sieci tak jak jest"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Nie używaj tej sieci"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Zaloguj się do sieci"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Zaloguj się w aplikacji %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"W sieci, z którą próbujesz się połączyć, występują problemy z zabezpieczeniami."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Na przykład strona logowania może nie należeć do wyświetlanej organizacji."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Kontynuuj mimo to w przeglądarce"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Certyfikat nie pochodzi od zaufanego urzędu."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Nazwa witryny nie pasuje do nazwy na certyfikacie."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Certyfikat wygasł."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Certyfikat nie jest jeszcze ważny."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Certyfikat ma nieprawidłową datę."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Certyfikat jest nieprawidłowy."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Nieznany błąd certyfikatu."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Ostrzeżenie dotyczące bezpieczeństwa"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Wyświetl certyfikat"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adres:"</string>
- <string name="page_info" msgid="4416941086705172545">"Informacje o stronie"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-pt-rBR/strings.xml b/packages/CaptivePortalLogin/res/values-pt-rBR/strings.xml
deleted file mode 100644
index 3aa82c4..0000000
--- a/packages/CaptivePortalLogin/res/values-pt-rBR/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Usar esta rede como está"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Não usar esta rede"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Fazer login na rede"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Fazer login em %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"A rede à qual você está tentando se conectar tem problemas de segurança."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Por exemplo, a página de login pode não pertencer à organização mostrada."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Continuar mesmo assim pelo navegador"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Esse certificado não é de uma autoridade confiável."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"O nome do site não corresponde ao nome no certificado."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Esse certificado expirou."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Esse certificado ainda não é válido."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"A data desse certificado é inválida."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Esse certificado é inválido."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Erro de certificado desconhecido."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Aviso de segurança"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Ver certificado"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Endereço:"</string>
- <string name="page_info" msgid="4416941086705172545">"Informações da página"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml b/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml
deleted file mode 100644
index 72d62a3..0000000
--- a/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Utilizar esta rede como está"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Não utilizar esta rede"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Início de sessão na rede"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Iniciar sessão em %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"A rede à qual está a tentar aceder tem problemas de segurança."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Por exemplo, a página de início de sessão pode não pertencer à entidade apresentada."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Continuar mesmo assim através do navegador"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Este certificado não pertence a uma autoridade fidedigna."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"O nome do site não corresponde ao nome constante no certificado."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Este certificado expirou."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Este certificado ainda não é válido."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Este certificado tem uma data inválida."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Este certificado é inválido."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Erro de certificado desconhecido."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Aviso de segurança"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Ver certificado"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Endereço:"</string>
- <string name="page_info" msgid="4416941086705172545">"Informações da página"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-pt/strings.xml b/packages/CaptivePortalLogin/res/values-pt/strings.xml
deleted file mode 100644
index 3aa82c4..0000000
--- a/packages/CaptivePortalLogin/res/values-pt/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Usar esta rede como está"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Não usar esta rede"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Fazer login na rede"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Fazer login em %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"A rede à qual você está tentando se conectar tem problemas de segurança."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Por exemplo, a página de login pode não pertencer à organização mostrada."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Continuar mesmo assim pelo navegador"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Esse certificado não é de uma autoridade confiável."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"O nome do site não corresponde ao nome no certificado."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Esse certificado expirou."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Esse certificado ainda não é válido."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"A data desse certificado é inválida."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Esse certificado é inválido."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Erro de certificado desconhecido."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Aviso de segurança"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Ver certificado"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Endereço:"</string>
- <string name="page_info" msgid="4416941086705172545">"Informações da página"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ro/strings.xml b/packages/CaptivePortalLogin/res/values-ro/strings.xml
deleted file mode 100644
index e3fc191..0000000
--- a/packages/CaptivePortalLogin/res/values-ro/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Utilizați această rețea în starea actuală"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Nu utilizați această rețea"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Conectați-vă la rețea"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Conectați-vă la %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Rețeaua la care încercați să vă conectați are probleme de securitate."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"De exemplu, este posibil ca pagina de conectare să nu aparțină organizației afișate."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Continuați oricum prin browser"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Acest certificat nu provine de la o autoritate de încredere."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Numele acestui site nu se potrivește cu numele de pe certificat."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Acest certificat a expirat."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Acest certificat nu este încă valid."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Acest certificat are o dată nevalidă."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Acest certificat este nevalid."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Eroare de certificat necunoscută."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Avertisment de securitate"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Vizualizați certificatul"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adresă:"</string>
- <string name="page_info" msgid="4416941086705172545">"Informații despre pagină"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ru/strings.xml b/packages/CaptivePortalLogin/res/values-ru/strings.xml
deleted file mode 100644
index a976f57..0000000
--- a/packages/CaptivePortalLogin/res/values-ru/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Использовать эту сеть"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Не использовать эту сеть"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Регистрация в сети"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Войти: %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Сеть, к которой вы хотите подключиться, небезопасна."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Например, страница входа в аккаунт может быть фиктивной."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Игнорировать и открыть браузер"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Этот сертификат получен из ненадежных источников."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Название сайта отличается от указанного в сертификате."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Срок действия сертификата истек."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Сертификат ещё не действителен."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Дата сертификата недействительна."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Сертификат недействителен."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Неизвестная ошибка, связанная с сертификатом."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Предупреждение об опасности"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Показать сертификат"</string>
- <string name="ok" msgid="2817931639040794018">"ОК"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Адрес:"</string>
- <string name="page_info" msgid="4416941086705172545">"О странице"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-si/strings.xml b/packages/CaptivePortalLogin/res/values-si/strings.xml
deleted file mode 100644
index edfe716..0000000
--- a/packages/CaptivePortalLogin/res/values-si/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"මෙම ජාලය ලෙසම භාවිතා කරන්න"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"මෙම ජාලය භාවිතා කරන්න එපා"</string>
- <string name="action_bar_label" msgid="917235635415966620">"ජාලයට පුරනය වන්න"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$s වෙත පුරන්න"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"ඔබ සම්බන්ධ වීමට උත්සහ කරන ජාලයේ ආරක්ෂක ගැටළු ඇත."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"උදාහරණයක් ලෙස, පුරනය වන පිටුව පෙන්වා ඇති සංවිධානයට අයිති නැති විය හැක."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"කෙසේ වුවත් බ්රවුසරය හරහා ඉදිරියට යන්න"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"මෙම සහතිකය විශ්වාසී අධිකාරියකින් නොවේ."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"අඩවියේ නම සහතිකයේ නමට නොගැළපේ."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"මෙම සහතිකය කල් ඉකුත් වී ඇත."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"මෙම සහතිකය තවම වලංගු නැත."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"මෙම සහතිකයට වලංගු නොවන දිනයක් ඇත."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"මෙම සහතිකය වලංගු නොවේ."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"නොදන්නා සහතික දෝෂයකි."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"ආරක්ෂක අවවාදයයි"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"සහතිකය බලන්න"</string>
- <string name="ok" msgid="2817931639040794018">"හරි"</string>
- <string name="page_info_address" msgid="1261481517455692363">"ලිපිනය:"</string>
- <string name="page_info" msgid="4416941086705172545">"පිටු තොරතුරු"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-sk/strings.xml b/packages/CaptivePortalLogin/res/values-sk/strings.xml
deleted file mode 100644
index c1100b8..0000000
--- a/packages/CaptivePortalLogin/res/values-sk/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Použiť túto sieť tak, ako je"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Túto sieť nepoužívať"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Prihlásiť sa do siete"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Prihláste sa do služby %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Sieť, ku ktorej sa pokúšate pripojiť, má problémy so zabezpečením"</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Napríklad prihlasovacia stránka nemusí patriť uvedenej organizácii."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Pokračovať pomocou prehliadača"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Tento certifikát nepochádza od dôveryhodnej autority."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Názov stránky sa nezhoduje s názvom uvedeným v certifikáte."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Platnosť certifikátu skončila."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Tento certifikát zatiaľ nie je platný."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Tento certifikát má neplatný dátum."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Tento certifikát je neplatný."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Neznáma chyba certifikátu."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Bezpečnostné upozornenie"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Zobraziť certifikát"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adresa:"</string>
- <string name="page_info" msgid="4416941086705172545">"Informácie o stránke"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-sl/strings.xml b/packages/CaptivePortalLogin/res/values-sl/strings.xml
deleted file mode 100644
index b254240..0000000
--- a/packages/CaptivePortalLogin/res/values-sl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Uporabljajte to omrežje, »kakršno je«"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Ne uporabljajte tega omrežja"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Prijavite se v omrežje"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Prijava v %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Omrežje, ki se mu poskušate pridružiti, ima varnostne težave."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Stran za prijavo na primer morda ne pripada prikazani organizaciji."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Vseeno nadaljuj v brskalniku"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Potrdila ni izdal zaupanja vreden overitelj."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Ime spletnega mesta se ne ujema z imenom na potrdilu."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Potrdilo je poteklo."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"To potrdilo še ni veljavno."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"To potrdilo ima neveljaven datum."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"To potrdilo ni veljavno."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Neznana napaka potrdila."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Varnostno opozorilo"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Prikaži potrdilo"</string>
- <string name="ok" msgid="2817931639040794018">"V redu"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Naslov:"</string>
- <string name="page_info" msgid="4416941086705172545">"Podatki o strani"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-sq/strings.xml b/packages/CaptivePortalLogin/res/values-sq/strings.xml
deleted file mode 100644
index a178c40..0000000
--- a/packages/CaptivePortalLogin/res/values-sq/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Përdore këtë rrjet siç është"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Mos e përdor këtë rrjet"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Identifikohu në rrjet"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Identifikohu në %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Rrjeti në të cilin po përpiqesh të bashkohesh ka probleme sigurie."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"për shembull, faqja e identifikimit mund të mos i përkasë organizatës së shfaqur."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Vazhdo gjithsesi nëpërmjet shfletuesit"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Kjo certifikatë nuk është nga një autoritet i besuar."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Emri i sajtit nuk përputhet me emrin në certifikatë."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Kjo certifikatë ka skaduar."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Kjo certifikatë nuk është ende e vlefshme."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Kjo certifikatë ka një datë të pavlefshme."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Kjo certifikatë është e pavlefshme."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Gabim i panjohur i certifikatës."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Paralajmërim për sigurinë"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Shiko certifikatën"</string>
- <string name="ok" msgid="2817931639040794018">"Në rregull"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adresa:"</string>
- <string name="page_info" msgid="4416941086705172545">"Informacionet e faqes"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-sr/strings.xml b/packages/CaptivePortalLogin/res/values-sr/strings.xml
deleted file mode 100644
index 6b08369..0000000
--- a/packages/CaptivePortalLogin/res/values-sr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Користи ову мрежу такву каква је"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Не користи ову мрежу"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Пријави ме на мрежу"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Пријавите се у: %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Мрежа којој покушавате да се придружите има безбедносних проблема."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"На пример, страница за пријављивање можда не припада приказаној организацији."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Ипак настави преко прегледача"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Овај сертификат не потиче из поузданог извора."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Назив сајта се не подудара са називом на сертификату."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Овај сертификат је истекао."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Овај сертификат још увек није важећи."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Датум овог сертификата је неважећи."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Овај сертификат је неважећи."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Непозната грешка сертификата."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Безбедносно упозорење"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Прикажи сертификат"</string>
- <string name="ok" msgid="2817931639040794018">"Потврди"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Адреса:"</string>
- <string name="page_info" msgid="4416941086705172545">"Информације о страници"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-sv/strings.xml b/packages/CaptivePortalLogin/res/values-sv/strings.xml
deleted file mode 100644
index 7fe5432..0000000
--- a/packages/CaptivePortalLogin/res/values-sv/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Använd det här nätverket som det är"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Använd inte det här nätverket"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Logga in på nätverket"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Logga in på %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Nätverket du försöker ansluta till har säkerhetsproblem."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Det kan t.ex. hända att inloggningssidan inte tillhör den organisation som visas."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Fortsätt ändå via webbläsaren"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Certifikatet kommer inte från en betrodd utfärdare."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Webbplatsens namn matchar inte namnet på certifikatet."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Det här certifikatet har löpt ut."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Det här certifikatet är inte giltigt än."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Datumet för det här certifikatet är ogiltigt."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Det här certifikatet är ogiltigt."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Okänt certifikatfel."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Säkerhetsvarning"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Visa certifikat"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adress:"</string>
- <string name="page_info" msgid="4416941086705172545">"Sidinformation"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-sw/strings.xml b/packages/CaptivePortalLogin/res/values-sw/strings.xml
deleted file mode 100644
index 297b72d..0000000
--- a/packages/CaptivePortalLogin/res/values-sw/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Tumia mtandao huu jinsi ulivyo"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Usitumie mtandao huu"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Ingia katika mtandao"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Ingia katika akaunti ya %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Mtandao unaojaribu kujiunga nao una matatizo ya usalama."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Kwa mfano, ukurasa wa kuingia katika akaunti unaweza usiwe unamilikiwa na shirika lililoonyeshwa."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Endelea hata hivyo kupitia kivinjari"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Cheti hiki hakijatoka kwa mamlaka inayoaminika."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Jina la tovuti halilingani na jina lililo katika cheti."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Muda wa kutumia cheti hiki umeisha."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Cheti hiki bado si halali."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Tarehe ya cheti hiki si sahihi."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Cheti hiki si sahihi."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Hitilafu isiyojulikana ya cheti."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Ilani ya usalama"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Angalia cheti"</string>
- <string name="ok" msgid="2817931639040794018">"Sawa"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Anwani:"</string>
- <string name="page_info" msgid="4416941086705172545">"Maelezo ya ukurasa"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ta/strings.xml b/packages/CaptivePortalLogin/res/values-ta/strings.xml
deleted file mode 100644
index 4517d15..0000000
--- a/packages/CaptivePortalLogin/res/values-ta/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"இந்த நெட்வொர்க்கைப் பயன்படுத்து"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"இந்த நெட்வொர்க்கைப் பயன்படுத்த வேண்டாம்"</string>
- <string name="action_bar_label" msgid="917235635415966620">"நெட்வொர்க்கில் உள்நுழையவும்"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$s இல் உள்நுழைக"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"நீங்கள் சேர முயற்சிக்கும் நெட்வொர்க்கில் பாதுகாப்புச் சிக்கல்கள் உள்ளன."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"எடுத்துக்காட்டாக, உள்நுழைவுப் பக்கமானது காட்டப்படும் அமைப்பிற்குச் சொந்தமானதாக இல்லாமல் இருக்கலாம்."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"பரவாயில்லை, உலாவி வழியாகத் தொடரவும்"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"இந்தச் சான்றிதழ் நம்பகமான நிறுவனத்தால் அங்கீகரிக்கப்படவில்லை."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"சான்றிதழிலுள்ள பெயருடன் வலைதளத்தின் பெயர் பொருந்தவில்லை."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"இந்தச் சான்றிதழ் காலாவதியாகிவிட்டது."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"இந்தச் சான்றிதழ் இன்னும் செல்லாததாக உள்ளது."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"இந்தச் சான்றிதழில் தவறான தேதி உள்ளது."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"இந்தச் சான்றிதழ் செல்லாதது."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"சான்றிதழில் அறியப்படாத பிழை உள்ளது."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"பாதுகாப்பு எச்சரிக்கை"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"சான்றிதழைக் காட்டு"</string>
- <string name="ok" msgid="2817931639040794018">"சரி"</string>
- <string name="page_info_address" msgid="1261481517455692363">"முகவரி:"</string>
- <string name="page_info" msgid="4416941086705172545">"பக்கத் தகவல்"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-te/strings.xml b/packages/CaptivePortalLogin/res/values-te/strings.xml
deleted file mode 100644
index 9bb4c11..0000000
--- a/packages/CaptivePortalLogin/res/values-te/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"ఈ నెట్వర్క్ని యథావిధిగా ఉపయోగించు"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"ఈ నెట్వర్క్ని ఉపయోగించవద్దు"</string>
- <string name="action_bar_label" msgid="917235635415966620">"నెట్వర్క్కి సైన్ ఇన్ చేయండి"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$sకి సైన్ ఇన్ చేయండి"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"మీరు చేరడానికి ప్రయత్నిస్తున్న నెట్వర్క్ భద్రతా సమస్యలను కలిగి ఉంది."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"ఉదాహరణకు, లాగిన్ పేజీ చూపిన సంస్థకు చెందినది కాకపోవచ్చు."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"ఏదేమైనా బ్రౌజర్ ద్వారా కొనసాగించు"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"ఈ సర్టిఫికెట్ విశ్వసనీయ అధికార సంస్థ నుండి కాదు."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"సైట్ యొక్క పేరు సర్టిఫికెట్లోని పేరుతో సరిపోలలేదు."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"ఈ సర్టిఫికెట్ గడువు ముగిసింది."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ఈ సర్టిఫికెట్ ఇప్పటికీ చెల్లదు."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"ఈ సర్టిఫికెట్ చెల్లని తేదీని కలిగి ఉంది."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"ఈ సర్టిఫికెట్ చెల్లుబాటు కాదు."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"తెలియని సర్టిఫికెట్ ఎర్రర్."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"భద్రతా హెచ్చరిక"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"సర్టిఫికెట్ని చూడండి"</string>
- <string name="ok" msgid="2817931639040794018">"సరే"</string>
- <string name="page_info_address" msgid="1261481517455692363">"చిరునామా:"</string>
- <string name="page_info" msgid="4416941086705172545">"పేజీ సమాచారం"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-th/strings.xml b/packages/CaptivePortalLogin/res/values-th/strings.xml
deleted file mode 100644
index 739b505..0000000
--- a/packages/CaptivePortalLogin/res/values-th/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"ใช้เครือข่ายนี้ตามที่เป็นอยู่"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"ไม่ใช้เครือข่ายนี้"</string>
- <string name="action_bar_label" msgid="917235635415966620">"ลงชื่อเข้าใช้เครือข่าย"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"ลงชื่อเข้าใช้ %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"เครือข่ายที่คุณพยายามเข้าร่วมมีปัญหาด้านความปลอดภัย"</string>
- <string name="ssl_error_example" msgid="647898534624078900">"ตัวอย่างเช่น หน้าเข้าสู่ระบบอาจไม่ใช่ขององค์กรที่แสดงไว้"</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"ดำเนินการต่อผ่านเบราว์เซอร์"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"ใบรับรองนี้ไม่ได้มาจากผู้ออกที่เชื่อถือได้"</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"ชื่อเว็บไซต์ไม่ตรงกับในใบรับรอง"</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"ใบรับรองนี้หมดอายุแล้ว"</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"ใบรับรองนี้ยังใช้งานไม่ได้"</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"วันที่ในใบรับรองนี้ไม่ถูกต้อง"</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"ใบรับรองนี้ไม่ถูกต้อง"</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"ข้อผิดพลาดใบรับรองที่ไม่รู้จัก"</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"การแจ้งเตือนเกี่ยวกับความปลอดภัย"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"ดูใบรับรอง"</string>
- <string name="ok" msgid="2817931639040794018">"ตกลง"</string>
- <string name="page_info_address" msgid="1261481517455692363">"ที่อยู่:"</string>
- <string name="page_info" msgid="4416941086705172545">"ข้อมูลหน้าเว็บ"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-tl/strings.xml b/packages/CaptivePortalLogin/res/values-tl/strings.xml
deleted file mode 100644
index 483a589..0000000
--- a/packages/CaptivePortalLogin/res/values-tl/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Gamitin ang network na ito nang walang pagbabago"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Huwag gamitin ang network na ito"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Mag-sign in sa network"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Mag-sign in sa %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"May mga isyu sa seguridad ang network kung saan mo sinusubukang sumali."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Halimbawa, maaaring hindi sa organisasyong ipinapakita ang page sa pag-log in."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Magpatuloy pa rin sa pamamagitan ng browser"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Ang certificate na ito ay mula sa hindi pinagkakatiwalaang awtoridad."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Ang pangalan ng site ay hindi tumutugma sa pangalan sa certificate."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Nag-expire na ang certificate na ito."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Hindi pa valid ang certificate na ito."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"May invalid na petsa ang certificate na ito."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Invalid ang certificate na ito."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Hindi kilalang error sa certificate."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Babala sa seguridad"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Tingnan ang certificate"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Address:"</string>
- <string name="page_info" msgid="4416941086705172545">"Impormasyon ng page"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-tr/strings.xml b/packages/CaptivePortalLogin/res/values-tr/strings.xml
deleted file mode 100644
index f51cfbd..0000000
--- a/packages/CaptivePortalLogin/res/values-tr/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Bu ağı olduğu gibi kullan"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Bu ağı kullanma"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Ağda oturum açın"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$s üzerinde oturum açın"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Katılmaya çalıştığınız ağda güvenlik sorunları var."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Örneğin, giriş sayfası, gösterilen kuruluşa ait olmayabilir."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Yine de tarayıcıyla devam et"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Bu sertifika güvenilir bir yetkiliden değil."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Sitenin adı sertifika üzerindeki adla eşleşmiyor."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Bu sertifikanın süresi dolmuş."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Bu sertifika henüz geçerli değil."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Bu sertifikanın tarihi geçersiz."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Bu sertifika geçersiz."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Bilinmeyen sertifika hatası."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Güvenlik uyarısı"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Sertifikayı göster"</string>
- <string name="ok" msgid="2817931639040794018">"Tamam"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Adres:"</string>
- <string name="page_info" msgid="4416941086705172545">"Sayfa bilgileri"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-uk/strings.xml b/packages/CaptivePortalLogin/res/values-uk/strings.xml
deleted file mode 100644
index c1a5d05..0000000
--- a/packages/CaptivePortalLogin/res/values-uk/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Використовувати цю мережу як є"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Не використовувати цю мережу"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Увійти в мережу"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Увійти в обліковий запис %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"У мережі, до якої ви намагаєтеся під’єднатись, є проблеми з безпекою."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Наприклад, сторінка входу може не належати вказаній організації."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Усе одно продовжити у веб-переглядачі"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Сертифікат видано ненадійним центром сертифікації."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Назва сайту не збігається з назвою в сертифікаті."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Термін дії сертифіката закінчився."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Цей сертифікат ще не дійсний."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Цей сертифікат має недійсну дату."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Цей сертифікат недійсний."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Помилка невідомого сертифіката."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Застереження про небезпеку"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Переглянути сертифікат"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Адреса:"</string>
- <string name="page_info" msgid="4416941086705172545">"Інформація про сторінку"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-ur/strings.xml b/packages/CaptivePortalLogin/res/values-ur/strings.xml
deleted file mode 100644
index e6f8539..0000000
--- a/packages/CaptivePortalLogin/res/values-ur/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"جوں کا توں اس نیٹ ورک کا استعمال کریں"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"اس نیٹ ورک کا استعمال نہ کریں"</string>
- <string name="action_bar_label" msgid="917235635415966620">"نیٹ ورک میں سائن ان کریں"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$s میں سائن ان کریں"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"جس نیٹ ورک میں آپ شامل ہونے کی کوشش کر رہے ہیں اس میں سیکیورٹی کے مسائل ہیں۔"</string>
- <string name="ssl_error_example" msgid="647898534624078900">"مثال کے طور پر ہو سکتا ہے کہ لاگ ان صفحہ دکھائی گئی تنظیم سے تعلق نہ رکھتا ہو۔"</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"براؤزر کے ذریعے بہرحال جاری رکھیں"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"یہ سرٹیفکیٹ قابل اعتماد اتھارٹی سے حاصل شدہ نہیں ہے۔"</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"سائٹ کا نام سرٹیفکیٹ پر موجود نام سے مماثل نہیں ہے۔"</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"اس سرٹیفکیٹ کی میعاد ختم ہو گئی ہے۔"</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"یہ سرٹیفکیٹ ابھی تک درست نہیں ہے۔"</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"اس سرٹیفکیٹ میں ایک غلط تاریخ ہے۔"</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"یہ سرٹیفیکیٹ غلط ہے۔"</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"سرٹیفکیٹ کی نامعلوم خرابی۔"</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"سیکیورٹی وارننگ"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"سرٹیفکیٹ دیکھیں"</string>
- <string name="ok" msgid="2817931639040794018">"ٹھیک ہے"</string>
- <string name="page_info_address" msgid="1261481517455692363">"پتہ:"</string>
- <string name="page_info" msgid="4416941086705172545">"صفحہ کی معلومات"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-uz/strings.xml b/packages/CaptivePortalLogin/res/values-uz/strings.xml
deleted file mode 100644
index 4009db6a..0000000
--- a/packages/CaptivePortalLogin/res/values-uz/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Ushbu tarmoqdan o‘z holicha foydalanilsin"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Ushbu tarmoqdan foydalanilmasin"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Tarmoqqa kirish"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"%1$s hisobiga kirish"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Siz ulanmoqchi bo‘lgan tarmoqda xavfsizlik bilan bog‘liq muammolar mavjud."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Masalan, tizimga kirish sahifasi ko‘rsatilgan tashkilotga tegishli bo‘lmasligi mumkin."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"E’tiborsiz qoldirilsin va brauzer ochilsin"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Bu sertifikat ishonchsiz manbadan olingan."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Sayt nomi sertifikatdagi nomga mos emas."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Bu sertifikat muddati tugagan."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Bu sertifikat hali yaroqli emas."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Bu sertifikatdagi sana xato."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Bu sertifikat yaroqsiz."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Sertifikatga aloqador notanish xato."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Xavfsizlikka oid ogohlantirish"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Sertifikatni ochish"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Manzil:"</string>
- <string name="page_info" msgid="4416941086705172545">"Sahifa haqida"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-vi/strings.xml b/packages/CaptivePortalLogin/res/values-vi/strings.xml
deleted file mode 100644
index bd4e96a..0000000
--- a/packages/CaptivePortalLogin/res/values-vi/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Sử dụng mạng này"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Không sử dụng mạng này"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Đăng nhập vào mạng"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Đăng nhập vào %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Mạng mà bạn đang cố gắng tham gia có vấn đề về bảo mật."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Ví dụ, trang đăng nhập có thể không thuộc về tổ chức được hiển thị."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Vẫn tiếp tục qua trình duyệt"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Chứng chỉ này không xuất phát từ tổ chức phát hành đáng tin cậy."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Tên của trang web không khớp với tên trên chứng chỉ."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Chứng chỉ này đã hết hạn."</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Chứng chỉ này chưa hợp lệ."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Chứng chỉ này có ngày không hợp lệ."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Chứng chỉ này không hợp lệ."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Lỗi chứng chỉ không xác định."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Cảnh báo bảo mật"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Xem chứng chỉ"</string>
- <string name="ok" msgid="2817931639040794018">"OK"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Địa chỉ:"</string>
- <string name="page_info" msgid="4416941086705172545">"Thông tin trang"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml b/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml
deleted file mode 100644
index 5beaa2e..0000000
--- a/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"直接使用此网络"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"不要使用此网络"</string>
- <string name="action_bar_label" msgid="917235635415966620">"登录到网络"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"登录%1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"您尝试加入的网络存在安全问题。"</string>
- <string name="ssl_error_example" msgid="647898534624078900">"例如,登录页面可能并不属于页面上显示的单位。"</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"仍然通过浏览器继续操作"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"该证书并非来自可信的授权中心。"</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"网站的名称与证书上的名称不一致。"</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"该证书已过期。"</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"该证书尚未生效。"</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"该证书的日期无效。"</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"该证书无效。"</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"未知证书错误。"</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"安全警告"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"查看证书"</string>
- <string name="ok" msgid="2817931639040794018">"确定"</string>
- <string name="page_info_address" msgid="1261481517455692363">"地址:"</string>
- <string name="page_info" msgid="4416941086705172545">"页面信息"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml b/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml
deleted file mode 100644
index 7987023..0000000
--- a/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"依照現況使用這個網絡"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"不要使用這個網絡"</string>
- <string name="action_bar_label" msgid="917235635415966620">"登入網絡"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"登入「%1$s」"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"您正在嘗試加入的網絡有安全性問題。"</string>
- <string name="ssl_error_example" msgid="647898534624078900">"例如,登入頁面並不屬於所顯示的機構。"</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"透過瀏覽器繼續"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"此憑證並非由信任的機構發出。"</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"網站名稱與憑證上的名稱不符。"</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"此憑證已過期。"</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"此憑證尚未生效。"</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"此憑證的日期無效。"</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"此憑證無效。"</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"不明的憑證錯誤。"</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"安全性警告"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"查看憑證"</string>
- <string name="ok" msgid="2817931639040794018">"確定"</string>
- <string name="page_info_address" msgid="1261481517455692363">"電郵地址:"</string>
- <string name="page_info" msgid="4416941086705172545">"頁面資料"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml b/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml
deleted file mode 100644
index 9f92f39..0000000
--- a/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"依現況使用這個網路"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"不使用這個網路"</string>
- <string name="action_bar_label" msgid="917235635415966620">"登入網路"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"登入 %1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"你嘗試加入的網路有安全問題。"</string>
- <string name="ssl_error_example" msgid="647898534624078900">"例如,登入網頁中顯示的機構可能並非該網頁實際隸屬的機構。"</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"透過瀏覽器繼續"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"這個憑證並非來自信任的授權單位。"</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"網站名稱與憑證上的名稱不相符。"</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"這個憑證已過期。"</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"這個憑證尚未生效。"</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"這個憑證的日期無效。"</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"這個憑證無效。"</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"不明的憑證錯誤。"</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"安全性警告"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"查看憑證"</string>
- <string name="ok" msgid="2817931639040794018">"確定"</string>
- <string name="page_info_address" msgid="1261481517455692363">"地址:"</string>
- <string name="page_info" msgid="4416941086705172545">"頁面資訊"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values-zu/strings.xml b/packages/CaptivePortalLogin/res/values-zu/strings.xml
deleted file mode 100644
index ebb0f54..0000000
--- a/packages/CaptivePortalLogin/res/values-zu/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="5934709770924185752">"I-CaptivePortalLogin"</string>
- <string name="action_use_network" msgid="6076184727448466030">"Sebenzisa le nethiwekhi njengoba injalo"</string>
- <string name="action_do_not_use_network" msgid="4577366536956516683">"Ungasebenzisi le nethiwekhi"</string>
- <string name="action_bar_label" msgid="917235635415966620">"Ngena ngemvume kunethiwekhi"</string>
- <string name="action_bar_title" msgid="5645564790486983117">"Ngena ngemvume ku-%1$s"</string>
- <string name="ssl_error_warning" msgid="6653188881418638872">"Inethiwekhi ozama ukuyijoyina inezinkinga zokuvikela."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Isibonelo, ikhasi lokungena ngemvume kungenzeka lingelenhlangano ebonisiwe."</string>
- <string name="ssl_error_continue" msgid="6492718244923937110">"Qhubeka noma kunjalo ngesiphequluli"</string>
- <string name="ssl_error_untrusted" msgid="1496280318271264520">"Lesi sitifiketi asisuki kusiphathimandla esithembekile."</string>
- <string name="ssl_error_mismatch" msgid="3060364165934822383">"Igama lesayithi alifani negama elikusitifiketi."</string>
- <string name="ssl_error_expired" msgid="1501588340716182495">"Lesi sitifiketi siphelelwe yisikhathi"</string>
- <string name="ssl_error_not_yet_valid" msgid="8648649030525886924">"Lesi sitifiketi asivumelekile okwamanje."</string>
- <string name="ssl_error_date_invalid" msgid="88425990680059223">"Lesi sitifiketi sinosuku olungalungile."</string>
- <string name="ssl_error_invalid" msgid="2540546515565633432">"Lesi sitifiketi asilungile."</string>
- <string name="ssl_error_unknown" msgid="4405203446079465859">"Iphutha lesitifiketi elingaziwa."</string>
- <string name="ssl_security_warning_title" msgid="8768539813847504404">"Isexwayiso sokuvikeleka"</string>
- <string name="ssl_error_view_certificate" msgid="5722652540168339333">"Buka isitifiketi"</string>
- <string name="ok" msgid="2817931639040794018">"KULUNGILE"</string>
- <string name="page_info_address" msgid="1261481517455692363">"Ikheli:"</string>
- <string name="page_info" msgid="4416941086705172545">"Ulwazi lekhasi"</string>
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values/dimens.xml b/packages/CaptivePortalLogin/res/values/dimens.xml
deleted file mode 100644
index 55c1e59..0000000
--- a/packages/CaptivePortalLogin/res/values/dimens.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<resources>
-
- <!-- Default screen margins, per the Android Design guidelines. -->
- <dimen name="activity_horizontal_margin">16dp</dimen>
- <dimen name="activity_vertical_margin">16dp</dimen>
-
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values/strings.xml b/packages/CaptivePortalLogin/res/values/strings.xml
deleted file mode 100644
index e9698db..0000000
--- a/packages/CaptivePortalLogin/res/values/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-
- <string name="app_name">CaptivePortalLogin</string>
- <string name="action_use_network">Use this network as is</string>
- <string name="action_do_not_use_network">Do not use this network</string>
- <string name="action_bar_label">Sign in to network</string>
- <string name="action_bar_title">Sign in to %1$s</string>
- <string name="ssl_error_warning">The network you’re trying to join has security issues.</string>
- <string name="ssl_error_example">For example, the login page may not belong to the organization shown.</string>
- <string name="ssl_error_continue">Continue anyway via browser</string>
- <string name="ssl_error_untrusted">This certificate isn\'t from a trusted authority.</string>
- <string name="ssl_error_mismatch">The name of the site doesn\'t match the name on the certificate.</string>
- <string name="ssl_error_expired">This certificate has expired.</string>
- <string name="ssl_error_not_yet_valid">This certificate isn\'t valid yet.</string>
- <string name="ssl_error_date_invalid">This certificate has an invalid date.</string>
- <string name="ssl_error_invalid">This certificate is invalid.</string>
- <string name="ssl_error_unknown">Unknown certificate error.</string>
- <string name="ssl_security_warning_title">Security warning</string>
- <string name="ssl_error_view_certificate">View certificate</string>
- <string name="ok">OK</string>
- <string name="page_info_address">Address:</string>
- <string name="page_info">Page info</string>
-
-</resources>
diff --git a/packages/CaptivePortalLogin/res/values/styles.xml b/packages/CaptivePortalLogin/res/values/styles.xml
deleted file mode 100644
index f6c2339..0000000
--- a/packages/CaptivePortalLogin/res/values/styles.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<resources>
-
- <!--
- Base application theme, dependent on API level. This theme is replaced
- by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
- -->
- <style name="AppBaseTheme" parent="@android:style/Theme.DeviceDefault.Settings">
- <!--
- Theme customizations available in newer API levels can go in
- res/values-vXX/styles.xml, while customizations related to
- backward-compatibility can go here.
- -->
- </style>
-
- <!-- Application theme. -->
- <style name="AppTheme" parent="AppBaseTheme">
- <!-- All customizations that are NOT specific to a particular API-level can go here. -->
- </style>
-</resources>
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
deleted file mode 100644
index 23fb7b6..0000000
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ /dev/null
@@ -1,710 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.captiveportallogin;
-
-import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_PROBE_SPEC;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Application;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.net.CaptivePortal;
-import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.net.Proxy;
-import android.net.Uri;
-import android.net.captiveportal.CaptivePortalProbeSpec;
-import android.net.http.SslCertificate;
-import android.net.http.SslError;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
-import android.os.Bundle;
-import android.os.SystemProperties;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.webkit.CookieManager;
-import android.webkit.SslErrorHandler;
-import android.webkit.WebChromeClient;
-import android.webkit.WebResourceRequest;
-import android.webkit.WebSettings;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
-import java.io.IOException;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Objects;
-import java.util.Random;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-public class CaptivePortalLoginActivity extends Activity {
- private static final String TAG = CaptivePortalLoginActivity.class.getSimpleName();
- private static final boolean DBG = true;
- private static final boolean VDBG = false;
-
- private static final int SOCKET_TIMEOUT_MS = 10000;
- public static final String HTTP_LOCATION_HEADER_NAME = "Location";
-
- private enum Result {
- DISMISSED(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_DISMISSED),
- UNWANTED(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_UNWANTED),
- WANTED_AS_IS(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_WANTED_AS_IS);
-
- final int metricsEvent;
- Result(int metricsEvent) { this.metricsEvent = metricsEvent; }
- };
-
- private URL mUrl;
- private CaptivePortalProbeSpec mProbeSpec;
- private String mUserAgent;
- private Network mNetwork;
- private CaptivePortal mCaptivePortal;
- private NetworkCallback mNetworkCallback;
- private ConnectivityManager mCm;
- private WifiManager mWifiManager;
- private boolean mLaunchBrowser = false;
- private MyWebViewClient mWebViewClient;
- private SwipeRefreshLayout mSwipeRefreshLayout;
- // Ensures that done() happens once exactly, handling concurrent callers with atomic operations.
- private final AtomicBoolean isDone = new AtomicBoolean(false);
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mCaptivePortal = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL);
- logMetricsEvent(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY);
-
- mCm = getSystemService(ConnectivityManager.class);
- mWifiManager = getSystemService(WifiManager.class);
- mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
- mUserAgent =
- getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT);
- mUrl = getUrl();
- if (mUrl == null) {
- // getUrl() failed to parse the url provided in the intent: bail out in a way that
- // at least provides network access.
- done(Result.WANTED_AS_IS);
- return;
- }
- if (DBG) {
- Log.d(TAG, String.format("onCreate for %s", mUrl.toString()));
- }
-
- final String spec = getIntent().getStringExtra(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC);
- try {
- mProbeSpec = CaptivePortalProbeSpec.parseSpecOrNull(spec);
- } catch (Exception e) {
- // Make extra sure that invalid configurations do not cause crashes
- mProbeSpec = null;
- }
-
- mNetworkCallback = new NetworkCallback() {
- @Override
- public void onLost(Network lostNetwork) {
- // If the network disappears while the app is up, exit.
- if (mNetwork.equals(lostNetwork)) done(Result.UNWANTED);
- }
- };
- mCm.registerNetworkCallback(new NetworkRequest.Builder().build(), mNetworkCallback);
-
- // If the network has disappeared, exit.
- final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork);
- if (networkCapabilities == null) {
- finishAndRemoveTask();
- return;
- }
-
- // Also initializes proxy system properties.
- mNetwork = mNetwork.getPrivateDnsBypassingCopy();
- mCm.bindProcessToNetwork(mNetwork);
-
- // Proxy system properties must be initialized before setContentView is called because
- // setContentView initializes the WebView logic which in turn reads the system properties.
- setContentView(R.layout.activity_captive_portal_login);
-
- getActionBar().setDisplayShowHomeEnabled(false);
- getActionBar().setElevation(0); // remove shadow
- getActionBar().setTitle(getHeaderTitle());
- getActionBar().setSubtitle("");
-
- final WebView webview = getWebview();
- webview.clearCache(true);
- CookieManager.getInstance().setAcceptThirdPartyCookies(webview, true);
- WebSettings webSettings = webview.getSettings();
- webSettings.setJavaScriptEnabled(true);
- webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
- webSettings.setUseWideViewPort(true);
- webSettings.setLoadWithOverviewMode(true);
- webSettings.setSupportZoom(true);
- webSettings.setBuiltInZoomControls(true);
- webSettings.setDisplayZoomControls(false);
- webSettings.setDomStorageEnabled(true);
- mWebViewClient = new MyWebViewClient();
- webview.setWebViewClient(mWebViewClient);
- webview.setWebChromeClient(new MyWebChromeClient());
- // Start initial page load so WebView finishes loading proxy settings.
- // Actual load of mUrl is initiated by MyWebViewClient.
- webview.loadData("", "text/html", null);
-
- mSwipeRefreshLayout = findViewById(R.id.swipe_refresh);
- mSwipeRefreshLayout.setOnRefreshListener(() -> {
- webview.reload();
- mSwipeRefreshLayout.setRefreshing(true);
- });
-
- }
-
- // Find WebView's proxy BroadcastReceiver and prompt it to read proxy system properties.
- private void setWebViewProxy() {
- // TODO: migrate to androidx WebView proxy setting API as soon as it is finalized
- try {
- final Field loadedApkField = Application.class.getDeclaredField("mLoadedApk");
- final Class<?> loadedApkClass = loadedApkField.getType();
- final Object loadedApk = loadedApkField.get(getApplication());
- Field receiversField = loadedApkClass.getDeclaredField("mReceivers");
- receiversField.setAccessible(true);
- ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk);
- for (Object receiverMap : receivers.values()) {
- for (Object rec : ((ArrayMap) receiverMap).keySet()) {
- Class clazz = rec.getClass();
- if (clazz.getName().contains("ProxyChangeListener")) {
- Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class,
- Intent.class);
- Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
- onReceiveMethod.invoke(rec, getApplicationContext(), intent);
- Log.v(TAG, "Prompting WebView proxy reload.");
- }
- }
- }
- } catch (Exception e) {
- Log.e(TAG, "Exception while setting WebView proxy: " + e);
- }
- }
-
- private void done(Result result) {
- if (isDone.getAndSet(true)) {
- // isDone was already true: done() already called
- return;
- }
- if (DBG) {
- Log.d(TAG, String.format("Result %s for %s", result.name(), mUrl.toString()));
- }
- logMetricsEvent(result.metricsEvent);
- switch (result) {
- case DISMISSED:
- mCaptivePortal.reportCaptivePortalDismissed();
- break;
- case UNWANTED:
- mCaptivePortal.ignoreNetwork();
- break;
- case WANTED_AS_IS:
- mCaptivePortal.useNetwork();
- break;
- }
- finishAndRemoveTask();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.captive_portal_login, menu);
- return true;
- }
-
- @Override
- public void onBackPressed() {
- WebView myWebView = findViewById(R.id.webview);
- if (myWebView.canGoBack() && mWebViewClient.allowBack()) {
- myWebView.goBack();
- } else {
- super.onBackPressed();
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- final Result result;
- final String action;
- final int id = item.getItemId();
- switch (id) {
- case R.id.action_use_network:
- result = Result.WANTED_AS_IS;
- action = "USE_NETWORK";
- break;
- case R.id.action_do_not_use_network:
- result = Result.UNWANTED;
- action = "DO_NOT_USE_NETWORK";
- break;
- default:
- return super.onOptionsItemSelected(item);
- }
- if (DBG) {
- Log.d(TAG, String.format("onOptionsItemSelect %s for %s", action, mUrl.toString()));
- }
- done(result);
- return true;
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- final WebView webview = (WebView) findViewById(R.id.webview);
- if (webview != null) {
- webview.stopLoading();
- webview.setWebViewClient(null);
- webview.setWebChromeClient(null);
- webview.destroy();
- }
- if (mNetworkCallback != null) {
- // mNetworkCallback is not null if mUrl is not null.
- mCm.unregisterNetworkCallback(mNetworkCallback);
- }
- if (mLaunchBrowser) {
- // Give time for this network to become default. After 500ms just proceed.
- for (int i = 0; i < 5; i++) {
- // TODO: This misses when mNetwork underlies a VPN.
- if (mNetwork.equals(mCm.getActiveNetwork())) break;
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- }
- }
- final String url = mUrl.toString();
- if (DBG) {
- Log.d(TAG, "starting activity with intent ACTION_VIEW for " + url);
- }
- startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
- }
- }
-
- private URL getUrl() {
- String url = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL);
- if (url == null) {
- url = mCm.getCaptivePortalServerUrl();
- }
- return makeURL(url);
- }
-
- private static URL makeURL(String url) {
- try {
- return new URL(url);
- } catch (MalformedURLException e) {
- Log.e(TAG, "Invalid URL " + url);
- }
- return null;
- }
-
- private static String host(URL url) {
- if (url == null) {
- return null;
- }
- return url.getHost();
- }
-
- private static String sanitizeURL(URL url) {
- // In non-Debug build, only show host to avoid leaking private info.
- return isDebuggable() ? Objects.toString(url) : host(url);
- }
-
- private static boolean isDebuggable() {
- return SystemProperties.getInt("ro.debuggable", 0) == 1;
- }
-
- private void testForCaptivePortal() {
- // TODO: reuse NetworkMonitor facilities for consistent captive portal detection.
- new Thread(new Runnable() {
- public void run() {
- // Give time for captive portal to open.
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- }
- HttpURLConnection urlConnection = null;
- int httpResponseCode = 500;
- String locationHeader = null;
- try {
- urlConnection = (HttpURLConnection) mNetwork.openConnection(mUrl);
- urlConnection.setInstanceFollowRedirects(false);
- urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
- urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
- urlConnection.setUseCaches(false);
- if (mUserAgent != null) {
- urlConnection.setRequestProperty("User-Agent", mUserAgent);
- }
- // cannot read request header after connection
- String requestHeader = urlConnection.getRequestProperties().toString();
-
- urlConnection.getInputStream();
- httpResponseCode = urlConnection.getResponseCode();
- locationHeader = urlConnection.getHeaderField(HTTP_LOCATION_HEADER_NAME);
- if (DBG) {
- Log.d(TAG, "probe at " + mUrl +
- " ret=" + httpResponseCode +
- " request=" + requestHeader +
- " headers=" + urlConnection.getHeaderFields());
- }
- } catch (IOException e) {
- } finally {
- if (urlConnection != null) urlConnection.disconnect();
- }
- if (isDismissed(httpResponseCode, locationHeader, mProbeSpec)) {
- done(Result.DISMISSED);
- }
- }
- }).start();
- }
-
- private static boolean isDismissed(
- int httpResponseCode, String locationHeader, CaptivePortalProbeSpec probeSpec) {
- return (probeSpec != null)
- ? probeSpec.getResult(httpResponseCode, locationHeader).isSuccessful()
- : (httpResponseCode == 204);
- }
-
- private class MyWebViewClient extends WebViewClient {
- private static final String INTERNAL_ASSETS = "file:///android_asset/";
-
- private final String mBrowserBailOutToken = Long.toString(new Random().nextLong());
- private final String mCertificateOutToken = Long.toString(new Random().nextLong());
- // How many Android device-independent-pixels per scaled-pixel
- // dp/sp = (px/sp) / (px/dp) = (1/sp) / (1/dp)
- private final float mDpPerSp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 1,
- getResources().getDisplayMetrics()) /
- TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
- getResources().getDisplayMetrics());
- private int mPagesLoaded;
- private String mMainFrameUrl;
-
- // If we haven't finished cleaning up the history, don't allow going back.
- public boolean allowBack() {
- return mPagesLoaded > 1;
- }
-
- private String mSslErrorTitle = null;
- private SslErrorHandler mSslErrorHandler = null;
- private SslError mSslError = null;
-
- @Override
- public void onPageStarted(WebView view, String urlString, Bitmap favicon) {
- if (urlString.contains(mBrowserBailOutToken)) {
- mLaunchBrowser = true;
- done(Result.WANTED_AS_IS);
- return;
- }
- // The first page load is used only to cause the WebView to
- // fetch the proxy settings. Don't update the URL bar, and
- // don't check if the captive portal is still there.
- if (mPagesLoaded == 0) {
- return;
- }
- final URL url = makeURL(urlString);
- Log.d(TAG, "onPageStarted: " + sanitizeURL(url));
- // For internally generated pages, leave URL bar listing prior URL as this is the URL
- // the page refers to.
- if (!urlString.startsWith(INTERNAL_ASSETS)) {
- String subtitle = (url != null) ? getHeaderSubtitle(url) : urlString;
- getActionBar().setSubtitle(subtitle);
- }
- getProgressBar().setVisibility(View.VISIBLE);
- testForCaptivePortal();
- }
-
- @Override
- public void onPageFinished(WebView view, String url) {
- mPagesLoaded++;
- getProgressBar().setVisibility(View.INVISIBLE);
- mSwipeRefreshLayout.setRefreshing(false);
- if (mPagesLoaded == 1) {
- // Now that WebView has loaded at least one page we know it has read in the proxy
- // settings. Now prompt the WebView read the Network-specific proxy settings.
- setWebViewProxy();
- // Load the real page.
- view.loadUrl(mUrl.toString());
- return;
- } else if (mPagesLoaded == 2) {
- // Prevent going back to empty first page.
- // Fix for missing focus, see b/62449959 for details. Remove it once we get a
- // newer version of WebView (60.x.y).
- view.requestFocus();
- view.clearHistory();
- }
- testForCaptivePortal();
- }
-
- // Convert Android scaled-pixels (sp) to HTML size.
- private String sp(int sp) {
- // Convert sp to dp's.
- float dp = sp * mDpPerSp;
- // Apply a scale factor to make things look right.
- dp *= 1.3;
- // Convert dp's to HTML size.
- // HTML px's are scaled just like dp's, so just add "px" suffix.
- return Integer.toString((int)dp) + "px";
- }
-
- // Check if webview is trying to load the main frame and record its url.
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
- if (request.isForMainFrame()) {
- mMainFrameUrl = request.getUrl().toString();
- }
- // Be careful that two shouldOverrideUrlLoading methods are overridden, but
- // shouldOverrideUrlLoading(WebView view, String url) was deprecated in API level 24.
- // TODO: delete deprecated one ??
- return shouldOverrideUrlLoading(view, mMainFrameUrl);
- }
-
- // A web page consisting of a large broken lock icon to indicate SSL failure.
-
- @Override
- public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
- final URL errorUrl = makeURL(error.getUrl());
- final URL mainFrameUrl = makeURL(mMainFrameUrl);
- Log.d(TAG, String.format("SSL error: %s, url: %s, certificate: %s",
- sslErrorName(error), sanitizeURL(errorUrl), error.getCertificate()));
- if (errorUrl == null
- // Ignore SSL errors from resources by comparing the main frame url with SSL
- // error url.
- || !errorUrl.equals(mainFrameUrl)) {
- Log.d(TAG, "onReceivedSslError: mMainFrameUrl = " + mMainFrameUrl);
- handler.cancel();
- return;
- }
- logMetricsEvent(MetricsEvent.CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR);
- final String sslErrorPage = makeSslErrorPage();
- view.loadDataWithBaseURL(INTERNAL_ASSETS, sslErrorPage, "text/HTML", "UTF-8", null);
- mSslErrorTitle = view.getTitle() == null ? "" : view.getTitle();
- mSslErrorHandler = handler;
- mSslError = error;
- }
-
- private String makeSslErrorPage() {
- final String warningMsg = getString(R.string.ssl_error_warning);
- final String exampleMsg = getString(R.string.ssl_error_example);
- final String continueMsg = getString(R.string.ssl_error_continue);
- final String certificateMsg = getString(R.string.ssl_error_view_certificate);
- return String.join("\n",
- "<html>",
- "<head>",
- " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">",
- " <style>",
- " body {",
- " background-color:#fafafa;",
- " margin:auto;",
- " width:80%;",
- " margin-top: 96px",
- " }",
- " img {",
- " height:48px;",
- " width:48px;",
- " }",
- " div.warn {",
- " font-size:" + sp(16) + ";",
- " line-height:1.28;",
- " margin-top:16px;",
- " opacity:0.87;",
- " }",
- " div.example {",
- " font-size:" + sp(14) + ";",
- " line-height:1.21905;",
- " margin-top:16px;",
- " opacity:0.54;",
- " }",
- " a {",
- " color:#4285F4;",
- " display:inline-block;",
- " font-size:" + sp(14) + ";",
- " font-weight:bold;",
- " height:48px;",
- " margin-top:24px;",
- " text-decoration:none;",
- " text-transform:uppercase;",
- " }",
- " a.certificate {",
- " margin-top:0px;",
- " }",
- " </style>",
- "</head>",
- "<body>",
- " <p><img src=quantum_ic_warning_amber_96.png><br>",
- " <div class=warn>" + warningMsg + "</div>",
- " <div class=example>" + exampleMsg + "</div>",
- " <a href=" + mBrowserBailOutToken + ">" + continueMsg + "</a><br>",
- " <a class=certificate href=" + mCertificateOutToken + ">" + certificateMsg +
- "</a>",
- "</body>",
- "</html>");
- }
-
- @Override
- public boolean shouldOverrideUrlLoading (WebView view, String url) {
- if (url.startsWith("tel:")) {
- startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(url)));
- return true;
- }
- if (url.contains(mCertificateOutToken) && mSslError != null) {
- showSslAlertDialog(mSslErrorHandler, mSslError, mSslErrorTitle);
- return true;
- }
- return false;
- }
- private void showSslAlertDialog(SslErrorHandler handler, SslError error, String title) {
- final LayoutInflater factory = LayoutInflater.from(CaptivePortalLoginActivity.this);
- final View sslWarningView = factory.inflate(R.layout.ssl_warning, null);
-
- // Set Security certificate
- setViewSecurityCertificate(sslWarningView.findViewById(R.id.certificate_layout), error);
- ((TextView) sslWarningView.findViewById(R.id.ssl_error_type))
- .setText(sslErrorName(error));
- ((TextView) sslWarningView.findViewById(R.id.title)).setText(mSslErrorTitle);
- ((TextView) sslWarningView.findViewById(R.id.address)).setText(error.getUrl());
-
- AlertDialog sslAlertDialog = new AlertDialog.Builder(CaptivePortalLoginActivity.this)
- .setTitle(R.string.ssl_security_warning_title)
- .setView(sslWarningView)
- .setPositiveButton(R.string.ok, (DialogInterface dialog, int whichButton) -> {
- // handler.cancel is called via OnCancelListener.
- dialog.cancel();
- })
- .setOnCancelListener((DialogInterface dialogInterface) -> handler.cancel())
- .create();
- sslAlertDialog.show();
- }
-
- private void setViewSecurityCertificate(LinearLayout certificateLayout, SslError error) {
- ((TextView) certificateLayout.findViewById(R.id.ssl_error_msg))
- .setText(sslErrorMessage(error));
- SslCertificate cert = error.getCertificate();
- // TODO: call the method directly once inflateCertificateView is @SystemApi
- try {
- final View certificateView = (View) SslCertificate.class.getMethod(
- "inflateCertificateView", Context.class)
- .invoke(cert, CaptivePortalLoginActivity.this);
- certificateLayout.addView(certificateView);
- } catch (ReflectiveOperationException | SecurityException e) {
- Log.e(TAG, "Could not create certificate view", e);
- }
- }
- }
-
- private class MyWebChromeClient extends WebChromeClient {
- @Override
- public void onProgressChanged(WebView view, int newProgress) {
- getProgressBar().setProgress(newProgress);
- }
- }
-
- private ProgressBar getProgressBar() {
- return findViewById(R.id.progress_bar);
- }
-
- private WebView getWebview() {
- return findViewById(R.id.webview);
- }
-
- private String getHeaderTitle() {
- NetworkCapabilities nc = mCm.getNetworkCapabilities(mNetwork);
- final String ssid = getSsid();
- if (TextUtils.isEmpty(ssid)
- || nc == null || !nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
- return getString(R.string.action_bar_label);
- }
- return getString(R.string.action_bar_title, ssid);
- }
-
- // TODO: remove once SSID is obtained from NetworkCapabilities
- private String getSsid() {
- if (mWifiManager == null) {
- return null;
- }
- final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
- return removeDoubleQuotes(wifiInfo.getSSID());
- }
-
- private static String removeDoubleQuotes(String string) {
- if (string == null) return null;
- final int length = string.length();
- if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
- return string.substring(1, length - 1);
- }
- return string;
- }
-
- private String getHeaderSubtitle(URL url) {
- String host = host(url);
- final String https = "https";
- if (https.equals(url.getProtocol())) {
- return https + "://" + host;
- }
- return host;
- }
-
- private void logMetricsEvent(int event) {
- mCaptivePortal.logEvent(event, getPackageName());
- }
-
- private static final SparseArray<String> SSL_ERRORS = new SparseArray<>();
- static {
- SSL_ERRORS.put(SslError.SSL_NOTYETVALID, "SSL_NOTYETVALID");
- SSL_ERRORS.put(SslError.SSL_EXPIRED, "SSL_EXPIRED");
- SSL_ERRORS.put(SslError.SSL_IDMISMATCH, "SSL_IDMISMATCH");
- SSL_ERRORS.put(SslError.SSL_UNTRUSTED, "SSL_UNTRUSTED");
- SSL_ERRORS.put(SslError.SSL_DATE_INVALID, "SSL_DATE_INVALID");
- SSL_ERRORS.put(SslError.SSL_INVALID, "SSL_INVALID");
- }
-
- private static String sslErrorName(SslError error) {
- return SSL_ERRORS.get(error.getPrimaryError(), "UNKNOWN");
- }
-
- private static final SparseArray<Integer> SSL_ERROR_MSGS = new SparseArray<>();
- static {
- SSL_ERROR_MSGS.put(SslError.SSL_NOTYETVALID, R.string.ssl_error_not_yet_valid);
- SSL_ERROR_MSGS.put(SslError.SSL_EXPIRED, R.string.ssl_error_expired);
- SSL_ERROR_MSGS.put(SslError.SSL_IDMISMATCH, R.string.ssl_error_mismatch);
- SSL_ERROR_MSGS.put(SslError.SSL_UNTRUSTED, R.string.ssl_error_untrusted);
- SSL_ERROR_MSGS.put(SslError.SSL_DATE_INVALID, R.string.ssl_error_date_invalid);
- SSL_ERROR_MSGS.put(SslError.SSL_INVALID, R.string.ssl_error_invalid);
- }
-
- private static Integer sslErrorMessage(SslError error) {
- return SSL_ERROR_MSGS.get(error.getPrimaryError(), R.string.ssl_error_unknown);
- }
-}
diff --git a/packages/CarSystemUI/res/layout/car_volume_item.xml b/packages/CarSystemUI/res/layout/car_volume_item.xml
index 2275ca6..0b42904 100644
--- a/packages/CarSystemUI/res/layout/car_volume_item.xml
+++ b/packages/CarSystemUI/res/layout/car_volume_item.xml
@@ -19,16 +19,18 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:background="@color/car_volume_dialog_background_color"
+ android:paddingStart="@dimen/car_volume_item_padding_start"
+ android:paddingEnd="@dimen/car_volume_item_padding_end"
android:minHeight="@dimen/car_volume_item_height">
<!-- Primary Action. -->
<ImageView
android:id="@+id/primary_icon"
- android:layout_width="@dimen/car_primary_icon_size"
+ android:layout_width="@dimen/car_volume_item_icon_size"
android:layout_centerVertical="true"
- android:layout_marginStart="@dimen/car_volume_item_margin_horizontal"
android:layout_alignParentStart="true"
- android:layout_height="@dimen/car_primary_icon_size"/>
+ android:layout_height="@dimen/car_volume_item_icon_size"/>
<!-- Note: the horizontal padding and offset are set to 0 so that the track and thumb
aligns with the proper keylines. -->
@@ -61,11 +63,10 @@
android:background="@color/car_volume_item_divider_color"/>
<ImageView
android:id="@+id/supplemental_icon"
- android:layout_width="@dimen/car_primary_icon_size"
- android:layout_height="@dimen/car_primary_icon_size"
+ android:layout_width="@dimen/car_volume_item_icon_size"
+ android:layout_height="@dimen/car_volume_item_icon_size"
android:background="?android:attr/selectableItemBackground"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true"
- android:layout_marginEnd="@dimen/car_volume_item_margin_horizontal"
android:scaleType="fitCenter"/>
</RelativeLayout>
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index 72914f7..e13c940 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -32,11 +32,14 @@
<color name="system_bar_background_opaque">#ff172026</color>
<color name="status_bar_background_color">#33000000</color>
- <drawable name="system_bar_background">@color/status_bar_background_color</drawable>
+ <drawable name="system_bar_background">@android:color/transparent</drawable>
<!-- The background color of the notification shade -->
<color name="notification_shade_background_color">#DD000000</color>
+ <!-- The background color of the car volume dialog -->
+ <color name="car_volume_dialog_background_color">@color/system_bar_background_opaque</color>
+
<!-- The color of the dividing line between grouped notifications. -->
<color name="notification_divider_color">@*android:color/notification_action_list</color>
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
index 0358357b..7017484 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -78,8 +78,10 @@
<dimen name="ongoing_appops_chip_bg_corner_radius">12dp</dimen>
<!-- Car volume dimens. -->
+ <dimen name="car_volume_item_icon_size">@dimen/car_primary_icon_size</dimen>
<dimen name="car_volume_item_height">@*android:dimen/car_single_line_list_item_height</dimen>
- <dimen name="car_volume_item_margin_horizontal">@*android:dimen/car_keyline_1</dimen>
+ <dimen name="car_volume_item_padding_start">@*android:dimen/car_keyline_1</dimen>
+ <dimen name="car_volume_item_padding_end">@*android:dimen/car_keyline_1</dimen>
<dimen name="car_volume_item_seekbar_margin_vertical">@*android:dimen/car_padding_1</dimen>
<dimen name="car_volume_item_seekbar_margin_start">@*android:dimen/car_keyline_3</dimen>
<dimen name="car_volume_item_seekbar_margin_end">@*android:dimen/car_padding_4</dimen>
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 7fbdc2e..8373761 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -1259,6 +1259,10 @@
@Override
protected void setHeadsUpVisible() {
+ // if the Notifications panel is showing don't show the Heads up
+ if (mPanelExpanded) {
+ return;
+ }
super.setHeadsUpVisible();
if (mHeadsUpPanel.getVisibility() == View.VISIBLE) {
mStatusBarWindowController.setHeadsUpShowing(true);
diff --git a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
index 94962f7..d0a63f0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
+++ b/packages/CarSystemUI/src/com/android/systemui/volume/CarVolumeDialogImpl.java
@@ -32,7 +32,9 @@
import android.content.ServiceConnection;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.graphics.Color;
import android.graphics.PixelFormat;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.os.Debug;
@@ -245,6 +247,7 @@
mExpanded = false;
mWindow = mDialog.getWindow();
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+ mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
diff --git a/packages/ExtServices/Android.bp b/packages/ExtServices/Android.bp
deleted file mode 100644
index 77972fe..0000000
--- a/packages/ExtServices/Android.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-android_library {
- name: "ExtServices-core",
- srcs: [
- "src/**/*.java",
- ],
- resource_dirs: [
- "res",
- ],
-
- manifest: "AndroidManifest.xml",
-}
\ No newline at end of file
diff --git a/packages/ExtServices/Android.mk b/packages/ExtServices/Android.mk
deleted file mode 100644
index a06dd86..0000000
--- a/packages/ExtServices/Android.mk
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := ExtServices
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-LOCAL_CERTIFICATE := platform
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.proguard
-
-LOCAL_PRIVILEGED_MODULE := true
-
-LOCAL_MIN_SDK_VERSION := 28
-
-include $(BUILD_PACKAGE)
-
-# Use the following include to make our test apk.
-ifeq ($(strip $(LOCAL_PACKAGE_OVERRIDES)),)
-include $(call all-makefiles-under, $(LOCAL_PATH))
-endif
-
diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml
deleted file mode 100644
index 8592fc9..0000000
--- a/packages/ExtServices/AndroidManifest.xml
+++ /dev/null
@@ -1,96 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- package="android.ext.services"
- android:versionCode="220000000"
- android:versionName="1"
- coreApp="true">
-
- <uses-permission android:name="android.permission.PROVIDE_RESOLVER_RANKER_SERVICE" />
- <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
-
- <uses-permission android:name="android.permission.MONITOR_DEFAULT_SMS_PACKAGE" />
- <uses-permission android:name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-
- <uses-sdk
- android:targetSdkVersion="28"
- />
-
- <application android:label="@string/app_name"
- android:defaultToDeviceProtectedStorage="true"
- android:directBootAware="true">
-
- <service android:name=".storage.CacheQuotaServiceImpl"
- android:permission="android.permission.BIND_CACHE_QUOTA_SERVICE">
- <intent-filter>
- <action android:name="android.app.usage.CacheQuotaService" />
- </intent-filter>
- </service>
-
- <service android:name=".resolver.LRResolverRankerService"
- android:permission="android.permission.BIND_RESOLVER_RANKER_SERVICE">
- <intent-filter android:priority="-1">
- <action android:name="android.service.resolver.ResolverRankerService" />
- </intent-filter>
- </service>
-
- <service android:name=".notification.Assistant"
- android:label="@string/notification_assistant"
- android:permission="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE"
- android:exported="true">
- <intent-filter>
- <action android:name="android.service.notification.NotificationAssistantService" />
- </intent-filter>
- </service>
-
- <service android:name=".autofill.AutofillFieldClassificationServiceImpl"
- android:permission="android.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE">
- <intent-filter>
- <action android:name="android.service.autofill.AutofillFieldClassificationService" />
- </intent-filter>
- <meta-data
- android:name="android.autofill.field_classification.default_algorithm"
- android:resource="@string/autofill_field_classification_default_algorithm" />
- <meta-data
- android:name="android.autofill.field_classification.available_algorithms"
- android:resource="@array/autofill_field_classification_available_algorithms" />
- </service>
-
- <service android:name=".sms.FinancialSmsServiceImpl"
- android:permission="android.permission.BIND_FINANCIAL_SMS_SERVICE">
- <intent-filter>
- <action android:name="android.service.sms.action.FINANCIAL_SERVICE_INTENT" />
- </intent-filter>
- </service>
-
- <service android:name=".watchdog.ExplicitHealthCheckServiceImpl"
- android:permission="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE">
- <intent-filter>
- <action android:name="android.service.watchdog.ExplicitHealthCheckService" />
- </intent-filter>
- </service>
-
- <activity android:name=".notification.CopyCodeActivity"
- android:exported="false"
- android:theme="@android:style/Theme.NoDisplay"/>
-
- <library android:name="android.ext.services"/>
- </application>
-
-</manifest>
diff --git a/packages/ExtServices/MODULE_LICENSE_APACHE2 b/packages/ExtServices/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/packages/ExtServices/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/packages/ExtServices/NOTICE b/packages/ExtServices/NOTICE
deleted file mode 100644
index c5b1efa..0000000
--- a/packages/ExtServices/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
- Copyright (c) 2005-2008, 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.
-
- 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.
-
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
diff --git a/packages/ExtServices/proguard.proguard b/packages/ExtServices/proguard.proguard
deleted file mode 100644
index e5dfbe1..0000000
--- a/packages/ExtServices/proguard.proguard
+++ /dev/null
@@ -1,7 +0,0 @@
--keepparameternames
--keepattributes Exceptions,InnerClasses,Signature,Deprecated,
- SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
-
--keep public class * {
- public protected *;
-}
diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml
deleted file mode 100644
index 58deb11..0000000
--- a/packages/ExtServices/res/values/strings.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name">Android Services Library</string>
-
- <string name="notification_assistant">Android Adaptive Notifications</string>
-
- <string name="autofill_field_classification_default_algorithm">EDIT_DISTANCE</string>
- <string-array name="autofill_field_classification_available_algorithms">
- <item>EDIT_DISTANCE</item>
- <item>EXACT_MATCH</item>
- </string-array>
-
- <!-- Action chip to copy a one time code to the user's clipboard [CHAR LIMIT=NONE]-->
- <string name="copy_code_desc">Copy \u201c<xliff:g id="code" example="12345">%1$s</xliff:g>\u201c</string>
- <!-- Toast to display when text is copied to the device clipboard [CHAR LIMIT=64]-->
- <string name="code_copied_to_clipboard">Code copied</string>
-
-</resources>
diff --git a/packages/ExtServices/src/android/ext/services/Version.java b/packages/ExtServices/src/android/ext/services/Version.java
deleted file mode 100644
index 026cccd..0000000
--- a/packages/ExtServices/src/android/ext/services/Version.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.ext.services;
-
-/**
- * Class that provides the version of the library.
- */
-public final class Version {
-
- private Version() {
- /* do nothing - hide constructor */
- }
-
- /**
- * Gets the version of the library.
- *
- * @return The version.
- */
- public static int getVersionCode() {
- return 1;
- }
-}
\ No newline at end of file
diff --git a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java b/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
deleted file mode 100644
index e379db8..0000000
--- a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
+++ /dev/null
@@ -1,95 +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 android.ext.services.autofill;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Bundle;
-import android.service.autofill.AutofillFieldClassificationService;
-import android.util.Log;
-import android.view.autofill.AutofillValue;
-
-import com.android.internal.util.ArrayUtils;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class AutofillFieldClassificationServiceImpl extends AutofillFieldClassificationService {
-
- private static final String TAG = "AutofillFieldClassificationServiceImpl";
-
- private static final String DEFAULT_ALGORITHM = REQUIRED_ALGORITHM_EDIT_DISTANCE;
-
- @Nullable
- @Override
- /** @hide */
- public float[][] onCalculateScores(@NonNull List<AutofillValue> actualValues,
- @NonNull List<String> userDataValues, @NonNull List<String> categoryIds,
- @Nullable String defaultAlgorithm, @Nullable Bundle defaultArgs,
- @Nullable Map algorithms, @Nullable Map args) {
- if (ArrayUtils.isEmpty(actualValues) || ArrayUtils.isEmpty(userDataValues)) {
- Log.w(TAG, "calculateScores(): empty currentvalues (" + actualValues
- + ") or userValues (" + userDataValues + ")");
- return null;
- }
-
- return calculateScores(actualValues, userDataValues, categoryIds, defaultAlgorithm,
- defaultArgs, (HashMap<String, String>) algorithms,
- (HashMap<String, Bundle>) args);
- }
-
- /** @hide */
- public float[][] calculateScores(@NonNull List<AutofillValue> actualValues,
- @NonNull List<String> userDataValues, @NonNull List<String> categoryIds,
- @Nullable String defaultAlgorithm, @Nullable Bundle defaultArgs,
- @Nullable HashMap<String, String> algorithms,
- @Nullable HashMap<String, Bundle> args) {
- final int actualValuesSize = actualValues.size();
- final int userDataValuesSize = userDataValues.size();
- final float[][] scores = new float[actualValuesSize][userDataValuesSize];
-
- for (int j = 0; j < userDataValuesSize; j++) {
- final String categoryId = categoryIds.get(j);
- String algorithmName = defaultAlgorithm;
- Bundle arg = defaultArgs;
- if (algorithms != null && algorithms.containsKey(categoryId)) {
- algorithmName = algorithms.get(categoryId);
- }
- if (args != null && args.containsKey(categoryId)) {
- arg = args.get(categoryId);
- }
-
- if (algorithmName == null || (!algorithmName.equals(DEFAULT_ALGORITHM)
- && !algorithmName.equals(REQUIRED_ALGORITHM_EXACT_MATCH))) {
- Log.w(TAG, "algorithmName is " + algorithmName + ", defaulting to "
- + DEFAULT_ALGORITHM);
- algorithmName = DEFAULT_ALGORITHM;
- }
-
- for (int i = 0; i < actualValuesSize; i++) {
- if (algorithmName.equals(DEFAULT_ALGORITHM)) {
- scores[i][j] = EditDistanceScorer.calculateScore(actualValues.get(i),
- userDataValues.get(j));
- } else {
- scores[i][j] = ExactMatch.calculateScore(actualValues.get(i),
- userDataValues.get(j), arg);
- }
- }
- }
- return scores;
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java b/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java
deleted file mode 100644
index 1962571..0000000
--- a/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.ext.services.autofill;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.view.autofill.AutofillValue;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-final class EditDistanceScorer {
-
- private static final String TAG = "EditDistanceScorer";
-
- // TODO(b/70291841): STOPSHIP - set to false before launching
- private static final boolean DEBUG = true;
-
- /**
- * Gets the field classification score of 2 values based on the edit distance between them.
- *
- * <p>The score is defined as: @(max_length - edit_distance) / max_length
- */
- @VisibleForTesting
- static float calculateScore(@Nullable AutofillValue actualValue,
- @Nullable String userDataValue) {
- if (actualValue == null || !actualValue.isText() || userDataValue == null) return 0;
-
- final String actualValueText = actualValue.getTextValue().toString();
- final int actualValueLength = actualValueText.length();
- final int userDatalength = userDataValue.length();
- if (userDatalength == 0) {
- return (actualValueLength == 0) ? 1 : 0;
- }
-
- final int distance = editDistance(actualValueText.toLowerCase(),
- userDataValue.toLowerCase());
- final int maxLength = Math.max(actualValueLength, userDatalength);
- return ((float) maxLength - distance) / maxLength;
- }
-
- /**
- * Computes the edit distance (number of insertions, deletions or substitutions to edit one
- * string into the other) between two strings. In particular, this will compute the Levenshtein
- * distance.
- *
- * <p>See http://en.wikipedia.org/wiki/Levenshtein_distance for details.
- *
- * @param s the first string to compare
- * @param t the second string to compare
- * @return the edit distance between the two strings
- */
- // Note: copied verbatim from com.android.tools.lint.detector.api.LintUtils.java
- public static int editDistance(@NonNull String s, @NonNull String t) {
- return editDistance(s, t, Integer.MAX_VALUE);
- }
-
- /**
- * Computes the edit distance (number of insertions, deletions or substitutions to edit one
- * string into the other) between two strings. In particular, this will compute the Levenshtein
- * distance.
- *
- * <p>See http://en.wikipedia.org/wiki/Levenshtein_distance for details.
- *
- * @param s the first string to compare
- * @param t the second string to compare
- * @param max the maximum edit distance that we care about; if for example the string length
- * delta is greater than this we don't bother computing the exact edit distance since the
- * caller has indicated they're not interested in the result
- * @return the edit distance between the two strings, or some other value greater than that if
- * the edit distance is at least as big as the {@code max} parameter
- */
- // Note: copied verbatim from com.android.tools.lint.detector.api.LintUtils.java
- public static int editDistance(@NonNull String s, @NonNull String t, int max) {
- if (s.equals(t)) {
- return 0;
- }
-
- if (Math.abs(s.length() - t.length()) > max) {
- // The string lengths differ more than the allowed edit distance;
- // no point in even attempting to compute the edit distance (requires
- // O(n*m) storage and O(n*m) speed, where n and m are the string lengths)
- return Integer.MAX_VALUE;
- }
-
- int m = s.length();
- int n = t.length();
- int[][] d = new int[m + 1][n + 1];
- for (int i = 0; i <= m; i++) {
- d[i][0] = i;
- }
- for (int j = 0; j <= n; j++) {
- d[0][j] = j;
- }
- for (int j = 1; j <= n; j++) {
- for (int i = 1; i <= m; i++) {
- if (s.charAt(i - 1) == t.charAt(j - 1)) {
- d[i][j] = d[i - 1][j - 1];
- } else {
- int deletion = d[i - 1][j] + 1;
- int insertion = d[i][j - 1] + 1;
- int substitution = d[i - 1][j - 1] + 1;
- d[i][j] = Math.min(deletion, Math.min(insertion, substitution));
- }
- }
- }
-
- return d[m][n];
- }
-
-}
diff --git a/packages/ExtServices/src/android/ext/services/autofill/ExactMatch.java b/packages/ExtServices/src/android/ext/services/autofill/ExactMatch.java
deleted file mode 100644
index 3e55c5c..0000000
--- a/packages/ExtServices/src/android/ext/services/autofill/ExactMatch.java
+++ /dev/null
@@ -1,67 +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 android.ext.services.autofill;
-
-import android.annotation.Nullable;
-import android.os.Bundle;
-import android.view.autofill.AutofillValue;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-final class ExactMatch {
-
- /**
- * Gets the field classification score of 2 values based on whether they are an exact match
- *
- * @return {@code 1.0} if the two values are an exact match, {@code 0.0} otherwise.
- */
- @VisibleForTesting
- static float calculateScore(@Nullable AutofillValue actualValue,
- @Nullable String userDataValue, @Nullable Bundle args) {
- if (actualValue == null || !actualValue.isText() || userDataValue == null) return 0;
-
- final String actualValueText = actualValue.getTextValue().toString();
-
- final int suffixLength;
- if (args != null) {
- suffixLength = args.getInt("suffix", -1);
-
- if (suffixLength < 0) {
- throw new IllegalArgumentException("suffix argument is invalid");
- }
-
- final String actualValueSuffix;
- if (suffixLength < actualValueText.length()) {
- actualValueSuffix = actualValueText.substring(actualValueText.length()
- - suffixLength);
- } else {
- actualValueSuffix = actualValueText;
- }
-
- final String userDataValueSuffix;
- if (suffixLength < userDataValue.length()) {
- userDataValueSuffix = userDataValue.substring(userDataValue.length()
- - suffixLength);
- } else {
- userDataValueSuffix = userDataValue;
- }
-
- return (actualValueSuffix.equalsIgnoreCase(userDataValueSuffix)) ? 1 : 0;
- } else {
- return actualValueText.equalsIgnoreCase(userDataValue) ? 1 : 0;
- }
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/AgingHelper.java b/packages/ExtServices/src/android/ext/services/notification/AgingHelper.java
deleted file mode 100644
index 31c9224..0000000
--- a/packages/ExtServices/src/android/ext/services/notification/AgingHelper.java
+++ /dev/null
@@ -1,172 +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 android.ext.services.notification;
-
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.ext.services.notification.NotificationCategorizer.Category;
-import android.net.Uri;
-import android.util.ArraySet;
-import android.util.Slog;
-
-import java.util.Set;
-
-public class AgingHelper {
- private final static String TAG = "AgingHelper";
- private final boolean DEBUG = false;
-
- private static final String AGING_ACTION = AgingHelper.class.getSimpleName() + ".EVALUATE";
- private static final int REQUEST_CODE_AGING = 1;
- private static final String AGING_SCHEME = "aging";
- private static final String EXTRA_KEY = "key";
- private static final String EXTRA_CATEGORY = "category";
-
- private static final int HOUR_MS = 1000 * 60 * 60;
- private static final int TWO_HOURS_MS = 2 * HOUR_MS;
-
- private Context mContext;
- private NotificationCategorizer mNotificationCategorizer;
- private AlarmManager mAm;
- private Callback mCallback;
-
- // The set of keys we've scheduled alarms for
- private Set<String> mAging = new ArraySet<>();
-
- public AgingHelper(Context context, NotificationCategorizer categorizer, Callback callback) {
- mNotificationCategorizer = categorizer;
- mContext = context;
- mAm = mContext.getSystemService(AlarmManager.class);
- mCallback = callback;
-
- IntentFilter filter = new IntentFilter(AGING_ACTION);
- filter.addDataScheme(AGING_SCHEME);
- mContext.registerReceiver(mBroadcastReceiver, filter);
- }
-
- // NAS lifecycle methods
-
- public void onNotificationSeen(NotificationEntry entry) {
- // user has strong opinions about this notification. we can't down rank it, so don't bother.
- if (entry.getChannel().hasUserSetImportance()) {
- return;
- }
-
- @Category int category = mNotificationCategorizer.getCategory(entry);
-
- // already very low
- if (category == NotificationCategorizer.CATEGORY_MIN) {
- return;
- }
-
- if (entry.hasSeen()) {
- if (category == NotificationCategorizer.CATEGORY_ONGOING
- || category > NotificationCategorizer.CATEGORY_REMINDER) {
- scheduleAging(entry.getSbn().getKey(), category, TWO_HOURS_MS);
- } else {
- scheduleAging(entry.getSbn().getKey(), category, HOUR_MS);
- }
-
- mAging.add(entry.getSbn().getKey());
- }
- }
-
- public void onNotificationPosted(NotificationEntry entry) {
- cancelAging(entry.getSbn().getKey());
- }
-
- public void onNotificationRemoved(String key) {
- cancelAging(key);
- }
-
- public void onDestroy() {
- mContext.unregisterReceiver(mBroadcastReceiver);
- }
-
- // Aging
-
- private void scheduleAging(String key, @Category int category, long duration) {
- if (mAging.contains(key)) {
- // already scheduled. Don't reset aging just because the user saw the noti again.
- return;
- }
- final PendingIntent pi = createPendingIntent(key, category);
- long time = System.currentTimeMillis() + duration;
- if (DEBUG) Slog.d(TAG, "Scheduling evaluate for " + key + " in ms: " + duration);
- mAm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, time, pi);
- }
-
- private void cancelAging(String key) {
- final PendingIntent pi = createPendingIntent(key);
- mAm.cancel(pi);
- mAging.remove(key);
- }
-
- private Intent createBaseIntent(String key) {
- return new Intent(AGING_ACTION)
- .setData(new Uri.Builder().scheme(AGING_SCHEME).appendPath(key).build());
- }
-
- private Intent createAgingIntent(String key, @Category int category) {
- Intent intent = createBaseIntent(key);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- .putExtra(EXTRA_CATEGORY, category)
- .putExtra(EXTRA_KEY, key);
- return intent;
- }
-
- private PendingIntent createPendingIntent(String key, @Category int category) {
- return PendingIntent.getBroadcast(mContext,
- REQUEST_CODE_AGING,
- createAgingIntent(key, category),
- PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- private PendingIntent createPendingIntent(String key) {
- return PendingIntent.getBroadcast(mContext,
- REQUEST_CODE_AGING,
- createBaseIntent(key),
- PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- private void demote(String key, @Category int category) {
- int newImportance = IMPORTANCE_MIN;
- // TODO: Change "aged" importance based on category
- mCallback.sendAdjustment(key, newImportance);
- }
-
- protected interface Callback {
- void sendAdjustment(String key, int newImportance);
- }
-
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DEBUG) {
- Slog.d(TAG, "Reposting notification");
- }
- if (AGING_ACTION.equals(intent.getAction())) {
- demote(intent.getStringExtra(EXTRA_KEY), intent.getIntExtra(EXTRA_CATEGORY,
- NotificationCategorizer.CATEGORY_EVERYTHING_ELSE));
- }
- }
- };
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
deleted file mode 100644
index d01878a..0000000
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ /dev/null
@@ -1,514 +0,0 @@
-/**
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.ext.services.notification;
-
-import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-import static android.service.notification.Adjustment.KEY_IMPORTANCE;
-import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
-import android.app.ActivityThread;
-import android.app.INotificationManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.content.Context;
-import android.content.pm.IPackageManager;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.UserHandle;
-import android.os.storage.StorageManager;
-import android.service.notification.Adjustment;
-import android.service.notification.NotificationAssistantService;
-import android.service.notification.NotificationStats;
-import android.service.notification.StatusBarNotification;
-import android.util.ArrayMap;
-import android.util.AtomicFile;
-import android.util.Log;
-import android.util.Slog;
-import android.util.Xml;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.XmlUtils;
-
-import libcore.io.IoUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/**
- * Notification assistant that provides guidance on notification channel blocking
- */
-@SuppressLint("OverrideAbstract")
-public class Assistant extends NotificationAssistantService {
- private static final String TAG = "ExtAssistant";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
- private static final String TAG_ASSISTANT = "assistant";
- private static final String TAG_IMPRESSION = "impression-set";
- private static final String ATT_KEY = "key";
- private static final int DB_VERSION = 1;
- private static final String ATTR_VERSION = "version";
- private final ExecutorService mSingleThreadExecutor = Executors.newSingleThreadExecutor();
-
- private static final ArrayList<Integer> PREJUDICAL_DISMISSALS = new ArrayList<>();
- static {
- PREJUDICAL_DISMISSALS.add(REASON_CANCEL);
- PREJUDICAL_DISMISSALS.add(REASON_LISTENER_CANCEL);
- }
-
- private SmartActionsHelper mSmartActionsHelper;
- private NotificationCategorizer mNotificationCategorizer;
-
- // key : impressions tracker
- // TODO: prune deleted channels and apps
- private final ArrayMap<String, ChannelImpressions> mkeyToImpressions = new ArrayMap<>();
- // SBN key : entry
- protected ArrayMap<String, NotificationEntry> mLiveNotifications = new ArrayMap<>();
-
- private Ranking mFakeRanking = null;
- private AtomicFile mFile = null;
- private IPackageManager mPackageManager;
-
- @VisibleForTesting
- protected AssistantSettings.Factory mSettingsFactory = AssistantSettings.FACTORY;
- @VisibleForTesting
- protected AssistantSettings mSettings;
- private SmsHelper mSmsHelper;
-
- public Assistant() {
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- // Contexts are correctly hooked up by the creation step, which is required for the observer
- // to be hooked up/initialized.
- mPackageManager = ActivityThread.getPackageManager();
- mSettings = mSettingsFactory.createAndRegister(mHandler,
- getApplicationContext().getContentResolver(), getUserId(), this::updateThresholds);
- mSmartActionsHelper = new SmartActionsHelper(getContext(), mSettings);
- mNotificationCategorizer = new NotificationCategorizer();
- mSmsHelper = new SmsHelper(this);
- mSmsHelper.initialize();
- }
-
- @Override
- public void onDestroy() {
- // This null check is only for the unit tests as ServiceTestCase.tearDown calls onDestroy
- // without having first called onCreate.
- if (mSmsHelper != null) {
- mSmsHelper.destroy();
- }
- super.onDestroy();
- }
-
- private void loadFile() {
- if (DEBUG) Slog.d(TAG, "loadFile");
- AsyncTask.execute(() -> {
- InputStream infile = null;
- try {
- infile = mFile.openRead();
- readXml(infile);
- } catch (FileNotFoundException e) {
- Log.d(TAG, "File doesn't exist or isn't readable yet");
- } catch (IOException e) {
- Log.e(TAG, "Unable to read channel impressions", e);
- } catch (NumberFormatException | XmlPullParserException e) {
- Log.e(TAG, "Unable to parse channel impressions", e);
- } finally {
- IoUtils.closeQuietly(infile);
- }
- });
- }
-
- protected void readXml(InputStream stream)
- throws XmlPullParserException, NumberFormatException, IOException {
- final XmlPullParser parser = Xml.newPullParser();
- parser.setInput(stream, StandardCharsets.UTF_8.name());
- final int outerDepth = parser.getDepth();
- while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- if (!TAG_ASSISTANT.equals(parser.getName())) {
- continue;
- }
- final int impressionOuterDepth = parser.getDepth();
- while (XmlUtils.nextElementWithin(parser, impressionOuterDepth)) {
- if (!TAG_IMPRESSION.equals(parser.getName())) {
- continue;
- }
- String key = parser.getAttributeValue(null, ATT_KEY);
- ChannelImpressions ci = createChannelImpressionsWithThresholds();
- ci.populateFromXml(parser);
- synchronized (mkeyToImpressions) {
- ci.append(mkeyToImpressions.get(key));
- mkeyToImpressions.put(key, ci);
- }
- }
- }
- }
-
- private void saveFile() {
- AsyncTask.execute(() -> {
- final FileOutputStream stream;
- try {
- stream = mFile.startWrite();
- } catch (IOException e) {
- Slog.w(TAG, "Failed to save policy file", e);
- return;
- }
- try {
- final XmlSerializer out = new FastXmlSerializer();
- out.setOutput(stream, StandardCharsets.UTF_8.name());
- writeXml(out);
- mFile.finishWrite(stream);
- } catch (IOException e) {
- Slog.w(TAG, "Failed to save impressions file, restoring backup", e);
- mFile.failWrite(stream);
- }
- });
- }
-
- protected void writeXml(XmlSerializer out) throws IOException {
- out.startDocument(null, true);
- out.startTag(null, TAG_ASSISTANT);
- out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
- synchronized (mkeyToImpressions) {
- for (Map.Entry<String, ChannelImpressions> entry
- : mkeyToImpressions.entrySet()) {
- // TODO: ensure channel still exists
- out.startTag(null, TAG_IMPRESSION);
- out.attribute(null, ATT_KEY, entry.getKey());
- entry.getValue().writeXml(out);
- out.endTag(null, TAG_IMPRESSION);
- }
- }
- out.endTag(null, TAG_ASSISTANT);
- out.endDocument();
- }
-
- @Override
- public Adjustment onNotificationEnqueued(StatusBarNotification sbn) {
- // we use the version with channel, so this is never called.
- return null;
- }
-
- @Override
- public Adjustment onNotificationEnqueued(StatusBarNotification sbn,
- NotificationChannel channel) {
- if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey() + " on " + channel.getId());
- if (!isForCurrentUser(sbn)) {
- return null;
- }
- mSingleThreadExecutor.submit(() -> {
- NotificationEntry entry =
- new NotificationEntry(getContext(), mPackageManager, sbn, channel, mSmsHelper);
- SmartActionsHelper.SmartSuggestions suggestions = mSmartActionsHelper.suggest(entry);
- if (DEBUG) {
- Log.d(TAG, String.format(
- "Creating Adjustment for %s, with %d actions, and %d replies.",
- sbn.getKey(), suggestions.actions.size(), suggestions.replies.size()));
- }
- Adjustment adjustment = createEnqueuedNotificationAdjustment(
- entry, suggestions.actions, suggestions.replies);
- adjustNotification(adjustment);
- });
- return null;
- }
-
- /** A convenience helper for creating an adjustment for an SBN. */
- @VisibleForTesting
- @Nullable
- Adjustment createEnqueuedNotificationAdjustment(
- @NonNull NotificationEntry entry,
- @NonNull ArrayList<Notification.Action> smartActions,
- @NonNull ArrayList<CharSequence> smartReplies) {
- Bundle signals = new Bundle();
-
- if (!smartActions.isEmpty()) {
- signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, smartActions);
- }
- if (!smartReplies.isEmpty()) {
- signals.putCharSequenceArrayList(Adjustment.KEY_TEXT_REPLIES, smartReplies);
- }
- if (mSettings.mNewInterruptionModel) {
- if (mNotificationCategorizer.shouldSilence(entry)) {
- final int importance = entry.getImportance() < IMPORTANCE_LOW
- ? entry.getImportance() : IMPORTANCE_LOW;
- signals.putInt(KEY_IMPORTANCE, importance);
- } else {
- // Even if no change is made, send an identity adjustment for metric logging.
- signals.putInt(KEY_IMPORTANCE, entry.getImportance());
- }
- }
-
- return new Adjustment(
- entry.getSbn().getPackageName(),
- entry.getSbn().getKey(),
- signals,
- "",
- entry.getSbn().getUserId());
- }
-
- @Override
- public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
- if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey());
- try {
- if (!isForCurrentUser(sbn)) {
- return;
- }
- Ranking ranking = getRanking(sbn.getKey(), rankingMap);
- if (ranking != null && ranking.getChannel() != null) {
- NotificationEntry entry = new NotificationEntry(getContext(), mPackageManager,
- sbn, ranking.getChannel(), mSmsHelper);
- String key = getKey(
- sbn.getPackageName(), sbn.getUserId(), ranking.getChannel().getId());
- boolean shouldTriggerBlock;
- synchronized (mkeyToImpressions) {
- ChannelImpressions ci = mkeyToImpressions.getOrDefault(key,
- createChannelImpressionsWithThresholds());
- mkeyToImpressions.put(key, ci);
- shouldTriggerBlock = ci.shouldTriggerBlock();
- }
- if (ranking.getImportance() > IMPORTANCE_MIN && shouldTriggerBlock) {
- adjustNotification(createNegativeAdjustment(
- sbn.getPackageName(), sbn.getKey(), sbn.getUserId()));
- }
- mLiveNotifications.put(sbn.getKey(), entry);
- }
- } catch (Throwable e) {
- Log.e(TAG, "Error occurred processing post", e);
- }
- }
-
- @Override
- public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
- NotificationStats stats, int reason) {
- try {
- if (!isForCurrentUser(sbn)) {
- return;
- }
-
- boolean updatedImpressions = false;
- String channelId = mLiveNotifications.remove(sbn.getKey()).getChannel().getId();
- String key = getKey(sbn.getPackageName(), sbn.getUserId(), channelId);
- synchronized (mkeyToImpressions) {
- ChannelImpressions ci = mkeyToImpressions.getOrDefault(key,
- createChannelImpressionsWithThresholds());
- if (stats != null && stats.hasSeen()) {
- ci.incrementViews();
- updatedImpressions = true;
- }
- if (PREJUDICAL_DISMISSALS.contains(reason)) {
- if ((!sbn.isAppGroup() || sbn.getNotification().isGroupChild())
- && !stats.hasInteracted()
- && stats.getDismissalSurface() != NotificationStats.DISMISSAL_AOD
- && stats.getDismissalSurface() != NotificationStats.DISMISSAL_PEEK
- && stats.getDismissalSurface() != NotificationStats.DISMISSAL_OTHER) {
- if (DEBUG) Log.i(TAG, "increment dismissals " + key);
- ci.incrementDismissals();
- updatedImpressions = true;
- } else {
- if (DEBUG) Slog.i(TAG, "reset streak " + key);
- if (ci.getStreak() > 0) {
- updatedImpressions = true;
- }
- ci.resetStreak();
- }
- }
- mkeyToImpressions.put(key, ci);
- }
- if (updatedImpressions) {
- saveFile();
- }
- } catch (Throwable e) {
- Slog.e(TAG, "Error occurred processing removal of " + sbn, e);
- }
- }
-
- @Override
- public void onNotificationSnoozedUntilContext(StatusBarNotification sbn,
- String snoozeCriterionId) {
- }
-
- @Override
- public void onNotificationsSeen(List<String> keys) {
- }
-
- @Override
- public void onNotificationExpansionChanged(@NonNull String key, boolean isUserAction,
- boolean isExpanded) {
- if (DEBUG) {
- Log.d(TAG, "onNotificationExpansionChanged() called with: key = [" + key
- + "], isUserAction = [" + isUserAction + "], isExpanded = [" + isExpanded
- + "]");
- }
- NotificationEntry entry = mLiveNotifications.get(key);
-
- if (entry != null) {
- mSingleThreadExecutor.submit(
- () -> mSmartActionsHelper.onNotificationExpansionChanged(entry, isExpanded));
- }
- }
-
- @Override
- public void onNotificationDirectReplied(@NonNull String key) {
- if (DEBUG) Log.i(TAG, "onNotificationDirectReplied " + key);
- mSingleThreadExecutor.submit(() -> mSmartActionsHelper.onNotificationDirectReplied(key));
- }
-
- @Override
- public void onSuggestedReplySent(@NonNull String key, @NonNull CharSequence reply,
- @Source int source) {
- if (DEBUG) {
- Log.d(TAG, "onSuggestedReplySent() called with: key = [" + key + "], reply = [" + reply
- + "], source = [" + source + "]");
- }
- mSingleThreadExecutor.submit(
- () -> mSmartActionsHelper.onSuggestedReplySent(key, reply, source));
- }
-
- @Override
- public void onActionInvoked(@NonNull String key, @NonNull Notification.Action action,
- @Source int source) {
- if (DEBUG) {
- Log.d(TAG,
- "onActionInvoked() called with: key = [" + key + "], action = [" + action.title
- + "], source = [" + source + "]");
- }
- mSingleThreadExecutor.submit(
- () -> mSmartActionsHelper.onActionClicked(key, action, source));
- }
-
- @Override
- public void onListenerConnected() {
- if (DEBUG) Log.i(TAG, "CONNECTED");
- try {
- mFile = new AtomicFile(new File(new File(
- Environment.getDataUserCePackageDirectory(
- StorageManager.UUID_PRIVATE_INTERNAL, getUserId(), getPackageName()),
- "assistant"), "blocking_helper_stats.xml"));
- loadFile();
- for (StatusBarNotification sbn : getActiveNotifications()) {
- onNotificationPosted(sbn);
- }
- } catch (Throwable e) {
- Log.e(TAG, "Error occurred on connection", e);
- }
- }
-
- @Override
- public void onListenerDisconnected() {
- }
-
- private boolean isForCurrentUser(StatusBarNotification sbn) {
- return sbn != null && sbn.getUserId() == UserHandle.myUserId();
- }
-
- protected String getKey(String pkg, int userId, String channelId) {
- return pkg + "|" + userId + "|" + channelId;
- }
-
- private Ranking getRanking(String key, RankingMap rankingMap) {
- if (mFakeRanking != null) {
- return mFakeRanking;
- }
- Ranking ranking = new Ranking();
- rankingMap.getRanking(key, ranking);
- return ranking;
- }
-
- private Adjustment createNegativeAdjustment(String packageName, String key, int user) {
- if (DEBUG) Log.d(TAG, "User probably doesn't want " + key);
- Bundle signals = new Bundle();
- signals.putInt(Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
- return new Adjustment(packageName, key, signals, "", user);
- }
-
- // for testing
-
- @VisibleForTesting
- public void setFile(AtomicFile file) {
- mFile = file;
- }
-
- @VisibleForTesting
- public void setFakeRanking(Ranking ranking) {
- mFakeRanking = ranking;
- }
-
- @VisibleForTesting
- public void setNoMan(INotificationManager noMan) {
- mNoMan = noMan;
- }
-
- @VisibleForTesting
- public void setContext(Context context) {
- mSystemContext = context;
- }
-
- @VisibleForTesting
- public void setPackageManager(IPackageManager pm) {
- mPackageManager = pm;
- }
-
- @VisibleForTesting
- public ChannelImpressions getImpressions(String key) {
- synchronized (mkeyToImpressions) {
- return mkeyToImpressions.get(key);
- }
- }
-
- @VisibleForTesting
- public void insertImpressions(String key, ChannelImpressions ci) {
- synchronized (mkeyToImpressions) {
- mkeyToImpressions.put(key, ci);
- }
- }
-
- private ChannelImpressions createChannelImpressionsWithThresholds() {
- ChannelImpressions impressions = new ChannelImpressions();
- impressions.updateThresholds(mSettings.mDismissToViewRatioLimit, mSettings.mStreakLimit);
- return impressions;
- }
-
- private void updateThresholds() {
- // Update all existing channel impression objects with any new limits/thresholds.
- synchronized (mkeyToImpressions) {
- for (ChannelImpressions channelImpressions: mkeyToImpressions.values()) {
- channelImpressions.updateThresholds(
- mSettings.mDismissToViewRatioLimit, mSettings.mStreakLimit);
- }
- }
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java b/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java
deleted file mode 100644
index 296db46..0000000
--- a/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java
+++ /dev/null
@@ -1,176 +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 android.ext.services.notification;
-
-import android.content.ContentResolver;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.provider.DeviceConfig;
-import android.provider.Settings;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-
-/**
- * Observes the settings for {@link Assistant}.
- */
-final class AssistantSettings extends ContentObserver {
- private static final String LOG_TAG = "AssistantSettings";
- public static Factory FACTORY = AssistantSettings::createAndRegister;
- private static final boolean DEFAULT_GENERATE_REPLIES = true;
- private static final boolean DEFAULT_GENERATE_ACTIONS = true;
- private static final int DEFAULT_NEW_INTERRUPTION_MODEL_INT = 1;
- private static final int DEFAULT_MAX_MESSAGES_TO_EXTRACT = 5;
- @VisibleForTesting
- static final int DEFAULT_MAX_SUGGESTIONS = 3;
-
- private static final Uri STREAK_LIMIT_URI =
- Settings.Global.getUriFor(Settings.Global.BLOCKING_HELPER_STREAK_LIMIT);
- private static final Uri DISMISS_TO_VIEW_RATIO_LIMIT_URI =
- Settings.Global.getUriFor(
- Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT);
- private static final Uri NOTIFICATION_NEW_INTERRUPTION_MODEL_URI =
- Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL);
-
- private final ContentResolver mResolver;
- private final int mUserId;
-
- private final Handler mHandler;
-
- @VisibleForTesting
- protected final Runnable mOnUpdateRunnable;
-
- // Actual configuration settings.
- float mDismissToViewRatioLimit;
- int mStreakLimit;
- boolean mGenerateReplies = DEFAULT_GENERATE_REPLIES;
- boolean mGenerateActions = DEFAULT_GENERATE_ACTIONS;
- boolean mNewInterruptionModel;
- int mMaxMessagesToExtract = DEFAULT_MAX_MESSAGES_TO_EXTRACT;
- int mMaxSuggestions = DEFAULT_MAX_SUGGESTIONS;
-
- private AssistantSettings(Handler handler, ContentResolver resolver, int userId,
- Runnable onUpdateRunnable) {
- super(handler);
- mHandler = handler;
- mResolver = resolver;
- mUserId = userId;
- mOnUpdateRunnable = onUpdateRunnable;
- }
-
- private static AssistantSettings createAndRegister(
- Handler handler, ContentResolver resolver, int userId, Runnable onUpdateRunnable) {
- AssistantSettings assistantSettings =
- new AssistantSettings(handler, resolver, userId, onUpdateRunnable);
- assistantSettings.register();
- assistantSettings.registerDeviceConfigs();
- return assistantSettings;
- }
-
- /**
- * Creates an instance but doesn't register it as an observer.
- */
- @VisibleForTesting
- protected static AssistantSettings createForTesting(
- Handler handler, ContentResolver resolver, int userId, Runnable onUpdateRunnable) {
- return new AssistantSettings(handler, resolver, userId, onUpdateRunnable);
- }
-
- private void register() {
- mResolver.registerContentObserver(
- DISMISS_TO_VIEW_RATIO_LIMIT_URI, false, this, mUserId);
- mResolver.registerContentObserver(STREAK_LIMIT_URI, false, this, mUserId);
-
- // Update all uris on creation.
- update(null);
- }
-
- private void registerDeviceConfigs() {
- DeviceConfig.addOnPropertiesChangedListener(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- this::postToHandler,
- (properties) -> onDeviceConfigPropertiesChanged(properties.getNamespace()));
-
- // Update the fields in this class from the current state of the device config.
- updateFromDeviceConfigFlags();
- }
-
- private void postToHandler(Runnable r) {
- this.mHandler.post(r);
- }
-
- @VisibleForTesting
- void onDeviceConfigPropertiesChanged(String namespace) {
- if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) {
- Log.e(LOG_TAG, "Received update from DeviceConfig for unrelated namespace: "
- + namespace);
- return;
- }
-
- updateFromDeviceConfigFlags();
- }
-
- private void updateFromDeviceConfigFlags() {
- mGenerateReplies = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, DEFAULT_GENERATE_REPLIES);
-
- mGenerateActions = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, DEFAULT_GENERATE_ACTIONS);
-
- mMaxMessagesToExtract = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_MAX_MESSAGES_TO_EXTRACT,
- DEFAULT_MAX_MESSAGES_TO_EXTRACT);
-
- mMaxSuggestions = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_MAX_SUGGESTIONS, DEFAULT_MAX_SUGGESTIONS);
-
- mOnUpdateRunnable.run();
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- update(uri);
- }
-
- private void update(Uri uri) {
- if (uri == null || DISMISS_TO_VIEW_RATIO_LIMIT_URI.equals(uri)) {
- mDismissToViewRatioLimit = Settings.Global.getFloat(
- mResolver, Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
- ChannelImpressions.DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT);
- }
- if (uri == null || STREAK_LIMIT_URI.equals(uri)) {
- mStreakLimit = Settings.Global.getInt(
- mResolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT,
- ChannelImpressions.DEFAULT_STREAK_LIMIT);
- }
- if (uri == null || NOTIFICATION_NEW_INTERRUPTION_MODEL_URI.equals(uri)) {
- int mNewInterruptionModelInt = Settings.Secure.getInt(
- mResolver, Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL,
- DEFAULT_NEW_INTERRUPTION_MODEL_INT);
- mNewInterruptionModel = mNewInterruptionModelInt == 1;
- }
-
- mOnUpdateRunnable.run();
- }
-
- public interface Factory {
- AssistantSettings createAndRegister(Handler handler, ContentResolver resolver, int userId,
- Runnable onUpdateRunnable);
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java b/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
deleted file mode 100644
index ca3daad..0000000
--- a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/**
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.ext.services.notification;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.IOException;
-
-public final class ChannelImpressions implements Parcelable {
- private static final String TAG = "ExtAssistant.CI";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
- static final float DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT = .8f;
- static final int DEFAULT_STREAK_LIMIT = 2;
- static final String ATT_DISMISSALS = "dismisses";
- static final String ATT_VIEWS = "views";
- static final String ATT_STREAK = "streak";
- static final String ATT_SENT = "sent";
- static final String ATT_INTERRUPTIVE = "interruptive";
-
- private int mDismissals = 0;
- private int mViews = 0;
- private int mStreak = 0;
-
- private float mDismissToViewRatioLimit;
- private int mStreakLimit;
-
- public ChannelImpressions() {
- mDismissToViewRatioLimit = DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT;
- mStreakLimit = DEFAULT_STREAK_LIMIT;
- }
-
- protected ChannelImpressions(Parcel in) {
- mDismissals = in.readInt();
- mViews = in.readInt();
- mStreak = in.readInt();
- mDismissToViewRatioLimit = in.readFloat();
- mStreakLimit = in.readInt();
- }
-
- public int getStreak() {
- return mStreak;
- }
-
- public int getDismissals() {
- return mDismissals;
- }
-
- public int getViews() {
- return mViews;
- }
-
- public void incrementDismissals() {
- mDismissals++;
- mStreak++;
- }
-
- void updateThresholds(float dismissToViewRatioLimit, int streakLimit) {
- mDismissToViewRatioLimit = dismissToViewRatioLimit;
- mStreakLimit = streakLimit;
- }
-
- @VisibleForTesting
- float getDismissToViewRatioLimit() {
- return mDismissToViewRatioLimit;
- }
-
- @VisibleForTesting
- int getStreakLimit() {
- return mStreakLimit;
- }
-
- public void append(ChannelImpressions additionalImpressions) {
- if (additionalImpressions != null) {
- mViews += additionalImpressions.getViews();
- mStreak += additionalImpressions.getStreak();
- mDismissals += additionalImpressions.getDismissals();
- }
- }
-
- public void incrementViews() {
- mViews++;
- }
-
- public void resetStreak() {
- mStreak = 0;
- }
-
- public boolean shouldTriggerBlock() {
- if (getViews() == 0) {
- return false;
- }
- if (DEBUG) {
- Log.d(TAG, "should trigger? " + getDismissals() + " " + getViews() + " " + getStreak());
- }
- return ((float) getDismissals() / getViews()) > mDismissToViewRatioLimit
- && getStreak() > mStreakLimit;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mDismissals);
- dest.writeInt(mViews);
- dest.writeInt(mStreak);
- dest.writeFloat(mDismissToViewRatioLimit);
- dest.writeInt(mStreakLimit);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- public static final Creator<ChannelImpressions> CREATOR = new Creator<ChannelImpressions>() {
- @Override
- public ChannelImpressions createFromParcel(Parcel in) {
- return new ChannelImpressions(in);
- }
-
- @Override
- public ChannelImpressions[] newArray(int size) {
- return new ChannelImpressions[size];
- }
- };
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- ChannelImpressions that = (ChannelImpressions) o;
-
- if (mDismissals != that.mDismissals) return false;
- if (mViews != that.mViews) return false;
- return mStreak == that.mStreak;
- }
-
- @Override
- public int hashCode() {
- int result = mDismissals;
- result = 31 * result + mViews;
- result = 31 * result + mStreak;
- return result;
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = new StringBuilder("ChannelImpressions{");
- sb.append("mDismissals=").append(mDismissals);
- sb.append(", mViews=").append(mViews);
- sb.append(", mStreak=").append(mStreak);
- sb.append(", thresholds=(").append(mDismissToViewRatioLimit);
- sb.append(",").append(mStreakLimit);
- sb.append(")}");
- return sb.toString();
- }
-
- protected void populateFromXml(XmlPullParser parser) {
- mDismissals = safeInt(parser, ATT_DISMISSALS, 0);
- mStreak = safeInt(parser, ATT_STREAK, 0);
- mViews = safeInt(parser, ATT_VIEWS, 0);
- }
-
- protected void writeXml(XmlSerializer out) throws IOException {
- if (mDismissals != 0) {
- out.attribute(null, ATT_DISMISSALS, String.valueOf(mDismissals));
- }
- if (mStreak != 0) {
- out.attribute(null, ATT_STREAK, String.valueOf(mStreak));
- }
- if (mViews != 0) {
- out.attribute(null, ATT_VIEWS, String.valueOf(mViews));
- }
- }
-
- private static int safeInt(XmlPullParser parser, String att, int defValue) {
- final String val = parser.getAttributeValue(null, att);
- return tryParseInt(val, defValue);
- }
-
- private static int tryParseInt(String value, int defValue) {
- if (TextUtils.isEmpty(value)) return defValue;
- try {
- return Integer.parseInt(value);
- } catch (NumberFormatException e) {
- return defValue;
- }
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/CopyCodeActivity.java b/packages/ExtServices/src/android/ext/services/notification/CopyCodeActivity.java
deleted file mode 100644
index 6708d4d..0000000
--- a/packages/ExtServices/src/android/ext/services/notification/CopyCodeActivity.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.ext.services.notification;
-
-import android.app.Activity;
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.content.Intent;
-import android.ext.services.R;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.Log;
-import android.widget.Toast;
-
-/**
- * An activity that copies text in the Bundle.
- */
-public class CopyCodeActivity extends Activity {
- private static final String TAG = "CopyCodeActivity";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- handleIntent();
- finish();
- }
-
- private void handleIntent() {
- String code = getIntent().getStringExtra(Intent.EXTRA_TEXT);
- if (TextUtils.isEmpty(code)) {
- Log.w(TAG, "handleIntent: empty code");
- return;
- }
- ClipboardManager clipboardManager = getSystemService(ClipboardManager.class);
- ClipData clipData = ClipData.newPlainText(null, code);
- clipboardManager.setPrimaryClip(clipData);
- Toast.makeText(getApplicationContext(), R.string.code_copied_to_clipboard,
- Toast.LENGTH_SHORT).show();
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/EntityTypeCounter.java b/packages/ExtServices/src/android/ext/services/notification/EntityTypeCounter.java
deleted file mode 100644
index 50cb0ab..0000000
--- a/packages/ExtServices/src/android/ext/services/notification/EntityTypeCounter.java
+++ /dev/null
@@ -1,73 +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 android.ext.services.notification;
-
-import android.annotation.NonNull;
-import android.util.ArrayMap;
-import android.view.textclassifier.TextClassifier;
-import android.view.textclassifier.TextLinks;
-
-/**
- * Counts the entity types for smart actions. Some entity types are considered the same
- * type, like {@link TextClassifier#TYPE_DATE} and {@link TextClassifier#TYPE_DATE_TIME}.
- */
-class EntityTypeCounter {
-
- private static final ArrayMap<String, String> ENTITY_TYPE_MAPPING = new ArrayMap<>();
-
- static {
- ENTITY_TYPE_MAPPING.put(TextClassifier.TYPE_DATE_TIME, TextClassifier.TYPE_DATE);
- }
-
- private final ArrayMap<String, Integer> mEntityTypeCount = new ArrayMap<>();
-
-
- void increment(@NonNull String entityType) {
- entityType = convertToBaseEntityType(entityType);
- if (mEntityTypeCount.containsKey(entityType)) {
- mEntityTypeCount.put(entityType, mEntityTypeCount.get(entityType) + 1);
- } else {
- mEntityTypeCount.put(entityType, 1);
- }
- }
-
- int getCount(@NonNull String entityType) {
- entityType = convertToBaseEntityType(entityType);
- return mEntityTypeCount.getOrDefault(entityType, 0);
- }
-
- @NonNull
- private String convertToBaseEntityType(@NonNull String entityType) {
- return ENTITY_TYPE_MAPPING.getOrDefault(entityType, entityType);
- }
-
- /**
- * Given the links extracted from a piece of text, returns the frequency of each entity
- * type.
- */
- @NonNull
- static EntityTypeCounter fromTextLinks(@NonNull TextLinks links) {
- EntityTypeCounter counter = new EntityTypeCounter();
- for (TextLinks.TextLink link : links.getLinks()) {
- if (link.getEntityCount() == 0) {
- continue;
- }
- String entityType = link.getEntity(0);
- counter.increment(entityType);
- }
- return counter;
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/NotificationCategorizer.java b/packages/ExtServices/src/android/ext/services/notification/NotificationCategorizer.java
deleted file mode 100644
index f95891c..0000000
--- a/packages/ExtServices/src/android/ext/services/notification/NotificationCategorizer.java
+++ /dev/null
@@ -1,109 +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 android.ext.services.notification;
-
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-
-import android.annotation.IntDef;
-import android.app.Notification;
-import android.media.AudioAttributes;
-import android.os.Process;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Default categorizer for incoming notifications; used to determine what notifications
- * should be silenced.
- */
-// TODO: stop using @hide methods
-public class NotificationCategorizer {
-
- protected static final int CATEGORY_MIN = -3;
- protected static final int CATEGORY_EVERYTHING_ELSE = -2;
- protected static final int CATEGORY_ONGOING = -1;
- protected static final int CATEGORY_SYSTEM_LOW = 0;
- protected static final int CATEGORY_EVENT = 1;
- protected static final int CATEGORY_REMINDER = 2;
- protected static final int CATEGORY_SYSTEM = 3;
- protected static final int CATEGORY_PEOPLE = 4;
- protected static final int CATEGORY_ALARM = 5;
- protected static final int CATEGORY_CALL = 6;
- protected static final int CATEGORY_HIGH = 7;
-
- /** @hide */
- @IntDef(prefix = { "CATEGORY_" }, value = {
- CATEGORY_MIN, CATEGORY_EVERYTHING_ELSE, CATEGORY_ONGOING, CATEGORY_CALL,
- CATEGORY_SYSTEM_LOW, CATEGORY_EVENT, CATEGORY_REMINDER, CATEGORY_SYSTEM,
- CATEGORY_PEOPLE, CATEGORY_ALARM, CATEGORY_HIGH
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface Category {}
-
- public boolean shouldSilence(NotificationEntry entry) {
- return shouldSilence(getCategory(entry));
- }
-
- @VisibleForTesting
- boolean shouldSilence(int category) {
- return category < CATEGORY_EVENT;
- }
-
- public int getCategory(NotificationEntry entry) {
- if (entry.getChannel() == null) {
- return CATEGORY_EVERYTHING_ELSE;
- }
- if (entry.getChannel().getImportance() == IMPORTANCE_MIN) {
- return CATEGORY_MIN;
- }
- if (entry.isCategory(Notification.CATEGORY_REMINDER)) {
- return CATEGORY_REMINDER;
- }
- if (entry.isCategory(Notification.CATEGORY_EVENT)) {
- return CATEGORY_EVENT;
- }
- if (entry.isCategory(Notification.CATEGORY_ALARM)
- || entry.isAudioAttributesUsage(AudioAttributes.USAGE_ALARM)) {
- return CATEGORY_ALARM;
- }
- // TODO: check for default phone app
- if (entry.isCategory(Notification.CATEGORY_CALL)) {
- return CATEGORY_CALL;
- }
- if (entry.involvesPeople()) {
- return CATEGORY_PEOPLE;
- }
- // TODO: is from signature app
- if (entry.getSbn().getUid() < Process.FIRST_APPLICATION_UID) {
- if (entry.getImportance() >= IMPORTANCE_DEFAULT) {
- return CATEGORY_SYSTEM;
- } else {
- return CATEGORY_SYSTEM_LOW;
- }
- }
- if (entry.getChannel().getImportance() == IMPORTANCE_HIGH) {
- return CATEGORY_HIGH;
- }
- if (entry.isOngoing()) {
- return CATEGORY_ONGOING;
- }
- return CATEGORY_EVERYTHING_ELSE;
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java b/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
deleted file mode 100644
index 1ffbac9..0000000
--- a/packages/ExtServices/src/android/ext/services/notification/NotificationEntry.java
+++ /dev/null
@@ -1,349 +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 android.ext.services.notification;
-
-import static android.app.Notification.CATEGORY_MESSAGE;
-import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.Person;
-import android.app.RemoteInput;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Icon;
-import android.media.AudioAttributes;
-import android.media.AudioSystem;
-import android.os.Build;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.service.notification.StatusBarNotification;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.util.ArrayList;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * Holds data about notifications.
- */
-public class NotificationEntry {
- static final String TAG = "NotificationEntry";
-
- // Copied from hidden definitions in Notification.TvExtender
- private static final String EXTRA_TV_EXTENDER = "android.tv.EXTENSIONS";
-
- private final Context mContext;
- private final StatusBarNotification mSbn;
- private final IPackageManager mPackageManager;
- private int mTargetSdkVersion = Build.VERSION_CODES.N_MR1;
- private final boolean mPreChannelsNotification;
- private final AudioAttributes mAttributes;
- private final NotificationChannel mChannel;
- private final int mImportance;
- private boolean mSeen;
- private boolean mIsShowActionEventLogged;
- private final SmsHelper mSmsHelper;
-
- private final Object mLock = new Object();
-
- public NotificationEntry(Context applicationContext, IPackageManager packageManager,
- StatusBarNotification sbn, NotificationChannel channel, SmsHelper smsHelper) {
- mContext = applicationContext;
- mSbn = cloneStatusBarNotificationLight(sbn);
- mChannel = channel;
- mPackageManager = packageManager;
- mPreChannelsNotification = isPreChannelsNotification();
- mAttributes = calculateAudioAttributes();
- mImportance = calculateInitialImportance();
- mSmsHelper = smsHelper;
- }
-
- /** Adapted from {@code Notification.lightenPayload}. */
- @SuppressWarnings("nullness")
- private static void lightenNotificationPayload(Notification notification) {
- notification.tickerView = null;
- notification.contentView = null;
- notification.bigContentView = null;
- notification.headsUpContentView = null;
- notification.largeIcon = null;
- if (notification.extras != null && !notification.extras.isEmpty()) {
- final Set<String> keyset = notification.extras.keySet();
- final int keysetSize = keyset.size();
- final String[] keys = keyset.toArray(new String[keysetSize]);
- for (int i = 0; i < keysetSize; i++) {
- final String key = keys[i];
- if (EXTRA_TV_EXTENDER.equals(key)
- || Notification.EXTRA_MESSAGES.equals(key)
- || Notification.EXTRA_MESSAGING_PERSON.equals(key)
- || Notification.EXTRA_PEOPLE_LIST.equals(key)) {
- continue;
- }
- final Object obj = notification.extras.get(key);
- if (obj != null
- && (obj instanceof Parcelable
- || obj instanceof Parcelable[]
- || obj instanceof SparseArray
- || obj instanceof ArrayList)) {
- notification.extras.remove(key);
- }
- }
- }
- }
-
- /** An interpretation of {@code Notification.cloneInto} with heavy=false. */
- private Notification cloneNotificationLight(Notification notification) {
- // We can't just use clone() here because the only way to remove the icons is with the
- // builder, which we can only create with a Context.
- Notification lightNotification =
- Notification.Builder.recoverBuilder(mContext, notification)
- .setSmallIcon(0)
- .setLargeIcon((Icon) null)
- .build();
- lightenNotificationPayload(lightNotification);
- return lightNotification;
- }
-
- /** Adapted from {@code StatusBarNotification.cloneLight}. */
- public StatusBarNotification cloneStatusBarNotificationLight(StatusBarNotification sbn) {
- return new StatusBarNotification(
- sbn.getPackageName(),
- sbn.getOpPkg(),
- sbn.getId(),
- sbn.getTag(),
- sbn.getUid(),
- /*initialPid=*/ 0,
- /*score=*/ 0,
- cloneNotificationLight(sbn.getNotification()),
- sbn.getUser(),
- sbn.getPostTime());
- }
-
- private boolean isPreChannelsNotification() {
- try {
- ApplicationInfo info = mPackageManager.getApplicationInfo(
- mSbn.getPackageName(), PackageManager.MATCH_ALL,
- mSbn.getUserId());
- if (info != null) {
- mTargetSdkVersion = info.targetSdkVersion;
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Couldn't look up " + mSbn.getPackageName());
- }
- if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(getChannel().getId())) {
- if (mTargetSdkVersion < Build.VERSION_CODES.O) {
- return true;
- }
- }
- return false;
- }
-
- private AudioAttributes calculateAudioAttributes() {
- final Notification n = getNotification();
- AudioAttributes attributes = getChannel().getAudioAttributes();
- if (attributes == null) {
- attributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
- }
-
- if (mPreChannelsNotification
- && (getChannel().getUserLockedFields()
- & NotificationChannel.USER_LOCKED_SOUND) == 0) {
- if (n.audioAttributes != null) {
- // prefer audio attributes to stream type
- attributes = n.audioAttributes;
- } else if (n.audioStreamType >= 0
- && n.audioStreamType < AudioSystem.getNumStreamTypes()) {
- // the stream type is valid, use it
- attributes = new AudioAttributes.Builder()
- .setInternalLegacyStreamType(n.audioStreamType)
- .build();
- } else if (n.audioStreamType != AudioSystem.STREAM_DEFAULT) {
- Log.w(TAG, String.format("Invalid stream type: %d", n.audioStreamType));
- }
- }
- return attributes;
- }
-
- private int calculateInitialImportance() {
- final Notification n = getNotification();
- int importance = getChannel().getImportance();
- int requestedImportance = IMPORTANCE_DEFAULT;
-
- // Migrate notification flags to scores
- if ((n.flags & Notification.FLAG_HIGH_PRIORITY) != 0) {
- n.priority = Notification.PRIORITY_MAX;
- }
-
- n.priority = clamp(n.priority, Notification.PRIORITY_MIN,
- Notification.PRIORITY_MAX);
- switch (n.priority) {
- case Notification.PRIORITY_MIN:
- requestedImportance = IMPORTANCE_MIN;
- break;
- case Notification.PRIORITY_LOW:
- requestedImportance = IMPORTANCE_LOW;
- break;
- case Notification.PRIORITY_DEFAULT:
- requestedImportance = IMPORTANCE_DEFAULT;
- break;
- case Notification.PRIORITY_HIGH:
- case Notification.PRIORITY_MAX:
- requestedImportance = IMPORTANCE_HIGH;
- break;
- }
-
- if (mPreChannelsNotification
- && (importance == IMPORTANCE_UNSPECIFIED
- || (getChannel().getUserLockedFields()
- & USER_LOCKED_IMPORTANCE) == 0)) {
- if (n.fullScreenIntent != null) {
- requestedImportance = IMPORTANCE_HIGH;
- }
- importance = requestedImportance;
- }
-
- return importance;
- }
-
- public boolean isCategory(String category) {
- return Objects.equals(getNotification().category, category);
- }
-
- /**
- * Similar to {@link #isCategory(String)}, but checking the public version of the notification,
- * if available.
- */
- public boolean isPublicVersionCategory(String category) {
- Notification publicVersion = getNotification().publicVersion;
- if (publicVersion == null) {
- return false;
- }
- return Objects.equals(publicVersion.category, category);
- }
-
- public boolean isAudioAttributesUsage(int usage) {
- return mAttributes != null && mAttributes.getUsage() == usage;
- }
-
- private boolean hasPerson() {
- // TODO: cache favorite and recent contacts to check contact affinity
- ArrayList<Person> people = getNotification().extras.getParcelableArrayList(
- Notification.EXTRA_PEOPLE_LIST);
- return people != null && !people.isEmpty();
- }
-
- protected boolean hasStyle(Class targetStyle) {
- Class<? extends Notification.Style> style = getNotification().getNotificationStyle();
- return targetStyle.equals(style);
- }
-
- protected boolean isOngoing() {
- return (getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
- }
-
- protected boolean involvesPeople() {
- return isMessaging()
- || hasStyle(Notification.InboxStyle.class)
- || hasPerson()
- || isDefaultSmsApp();
- }
-
- private boolean isDefaultSmsApp() {
- ComponentName defaultSmsApp = mSmsHelper.getDefaultSmsApplication();
- if (defaultSmsApp == null) {
- return false;
- }
- return mSbn.getPackageName().equals(defaultSmsApp.getPackageName());
- }
-
- protected boolean isMessaging() {
- return isCategory(CATEGORY_MESSAGE)
- || isPublicVersionCategory(CATEGORY_MESSAGE)
- || hasStyle(Notification.MessagingStyle.class);
- }
-
- public boolean hasInlineReply() {
- Notification.Action[] actions = getNotification().actions;
- if (actions == null) {
- return false;
- }
- for (Notification.Action action : actions) {
- RemoteInput[] remoteInputs = action.getRemoteInputs();
- if (remoteInputs == null) {
- continue;
- }
- for (RemoteInput remoteInput : remoteInputs) {
- if (remoteInput.getAllowFreeFormInput()) {
- return true;
- }
- }
- }
- return false;
- }
-
- public void setSeen() {
- synchronized (mLock) {
- mSeen = true;
- }
- }
-
- public void setShowActionEventLogged() {
- synchronized (mLock) {
- mIsShowActionEventLogged = true;
- }
- }
-
- public boolean hasSeen() {
- synchronized (mLock) {
- return mSeen;
- }
- }
-
- public boolean isShowActionEventLogged() {
- synchronized (mLock) {
- return mIsShowActionEventLogged;
- }
- }
-
- public StatusBarNotification getSbn() {
- return mSbn;
- }
-
- public Notification getNotification() {
- return getSbn().getNotification();
- }
-
- public NotificationChannel getChannel() {
- return mChannel;
- }
-
- public int getImportance() {
- return mImportance;
- }
-
- private int clamp(int x, int low, int high) {
- return (x < low) ? low : ((x > high) ? high : x);
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
deleted file mode 100644
index 93c522e..0000000
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ /dev/null
@@ -1,502 +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 android.ext.services.notification;
-
-import android.annotation.Nullable;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.Person;
-import android.app.RemoteAction;
-import android.app.RemoteInput;
-import android.content.Context;
-import android.content.Intent;
-import android.ext.services.R;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.os.Process;
-import android.service.notification.NotificationAssistantService;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.LruCache;
-import android.util.Pair;
-import android.view.textclassifier.ConversationAction;
-import android.view.textclassifier.ConversationActions;
-import android.view.textclassifier.TextClassificationContext;
-import android.view.textclassifier.TextClassificationManager;
-import android.view.textclassifier.TextClassifier;
-import android.view.textclassifier.TextClassifierEvent;
-
-import com.android.internal.util.ArrayUtils;
-
-import java.time.Instant;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Deque;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Generates suggestions from incoming notifications.
- *
- * Methods in this class should be called in a single worker thread.
- */
-public class SmartActionsHelper {
- static final String ENTITIES_EXTRAS = "entities-extras";
- static final String KEY_ACTION_TYPE = "action_type";
- static final String KEY_ACTION_SCORE = "action_score";
- static final String KEY_TEXT = "text";
- // If a notification has any of these flags set, it's inelgibile for actions being added.
- private static final int FLAG_MASK_INELGIBILE_FOR_ACTIONS =
- Notification.FLAG_ONGOING_EVENT
- | Notification.FLAG_FOREGROUND_SERVICE
- | Notification.FLAG_GROUP_SUMMARY
- | Notification.FLAG_NO_CLEAR;
- private static final int MAX_RESULT_ID_TO_CACHE = 20;
-
- private static final List<String> HINTS =
- Collections.singletonList(ConversationActions.Request.HINT_FOR_NOTIFICATION);
- private static final ConversationActions EMPTY_CONVERSATION_ACTIONS =
- new ConversationActions(Collections.emptyList(), null);
-
- private Context mContext;
- private TextClassificationManager mTextClassificationManager;
- private AssistantSettings mSettings;
- private LruCache<String, Session> mSessionCache = new LruCache<>(MAX_RESULT_ID_TO_CACHE);
-
- SmartActionsHelper(Context context, AssistantSettings settings) {
- mContext = context;
- mTextClassificationManager = mContext.getSystemService(TextClassificationManager.class);
- mSettings = settings;
- }
-
- SmartSuggestions suggest(NotificationEntry entry) {
- // Whenever suggest() is called on a notification, its previous session is ended.
- mSessionCache.remove(entry.getSbn().getKey());
-
- boolean eligibleForReplyAdjustment =
- mSettings.mGenerateReplies && isEligibleForReplyAdjustment(entry);
- boolean eligibleForActionAdjustment =
- mSettings.mGenerateActions && isEligibleForActionAdjustment(entry);
-
- ConversationActions conversationActionsResult =
- suggestConversationActions(
- entry,
- eligibleForReplyAdjustment,
- eligibleForActionAdjustment);
-
- String resultId = conversationActionsResult.getId();
- List<ConversationAction> conversationActions =
- conversationActionsResult.getConversationActions();
-
- ArrayList<CharSequence> replies = new ArrayList<>();
- Map<CharSequence, Float> repliesScore = new ArrayMap<>();
- for (ConversationAction conversationAction : conversationActions) {
- CharSequence textReply = conversationAction.getTextReply();
- if (TextUtils.isEmpty(textReply)) {
- continue;
- }
- replies.add(textReply);
- repliesScore.put(textReply, conversationAction.getConfidenceScore());
- }
-
- ArrayList<Notification.Action> actions = new ArrayList<>();
- for (ConversationAction conversationAction : conversationActions) {
- if (!TextUtils.isEmpty(conversationAction.getTextReply())) {
- continue;
- }
- Notification.Action notificationAction;
- if (conversationAction.getAction() == null) {
- notificationAction =
- createNotificationActionWithoutRemoteAction(conversationAction);
- } else {
- notificationAction = createNotificationActionFromRemoteAction(
- conversationAction.getAction(),
- conversationAction.getType(),
- conversationAction.getConfidenceScore());
- }
- if (notificationAction != null) {
- actions.add(notificationAction);
- }
- }
-
- // Start a new session for logging if necessary.
- if (!TextUtils.isEmpty(resultId)
- && !conversationActions.isEmpty()
- && suggestionsMightBeUsedInNotification(
- entry, !actions.isEmpty(), !replies.isEmpty())) {
- mSessionCache.put(entry.getSbn().getKey(), new Session(resultId, repliesScore));
- }
-
- return new SmartSuggestions(replies, actions);
- }
-
- /**
- * Creates notification action from ConversationAction that does not come up a RemoteAction.
- * It could happen because we don't have common intents for some actions, like copying text.
- */
- @Nullable
- private Notification.Action createNotificationActionWithoutRemoteAction(
- ConversationAction conversationAction) {
- if (ConversationAction.TYPE_COPY.equals(conversationAction.getType())) {
- return createCopyCodeAction(conversationAction);
- }
- return null;
- }
-
- @Nullable
- private Notification.Action createCopyCodeAction(ConversationAction conversationAction) {
- Bundle extras = conversationAction.getExtras();
- if (extras == null) {
- return null;
- }
- Bundle entitiesExtas = extras.getParcelable(ENTITIES_EXTRAS);
- if (entitiesExtas == null) {
- return null;
- }
- String code = entitiesExtas.getString(KEY_TEXT);
- if (TextUtils.isEmpty(code)) {
- return null;
- }
- String contentDescription = mContext.getString(R.string.copy_code_desc, code);
- Intent intent = new Intent(mContext, CopyCodeActivity.class);
- intent.putExtra(Intent.EXTRA_TEXT, code);
-
- RemoteAction remoteAction = new RemoteAction(Icon.createWithResource(
- mContext.getResources(),
- com.android.internal.R.drawable.ic_menu_copy_material),
- code,
- contentDescription,
- PendingIntent.getActivity(
- mContext,
- code.hashCode(),
- intent,
- PendingIntent.FLAG_UPDATE_CURRENT
- ));
-
- return createNotificationActionFromRemoteAction(
- remoteAction,
- ConversationAction.TYPE_COPY,
- conversationAction.getConfidenceScore());
- }
-
- /**
- * Returns whether the suggestion might be used in the notifications in SysUI.
- * <p>
- * Currently, NAS has no idea if suggestions will actually be used in the notification, and thus
- * this function tries to make a heuristic. This function tries to optimize the precision,
- * that means when it is unsure, it will return false. The objective is to avoid false positive,
- * which could pollute the log and CTR as we are logging click rate of suggestions that could
- * be never visible to users. On the other hand, it is fine to have false negative because
- * it would be just like sampling.
- */
- private boolean suggestionsMightBeUsedInNotification(
- NotificationEntry notificationEntry, boolean hasSmartAction, boolean hasSmartReply) {
- Notification notification = notificationEntry.getNotification();
- boolean hasAppGeneratedContextualActions = !notification.getContextualActions().isEmpty();
-
- Pair<RemoteInput, Notification.Action> freeformRemoteInputAndAction =
- notification.findRemoteInputActionPair(/* requiresFreeform */ true);
- boolean hasAppGeneratedReplies = false;
- boolean allowGeneratedReplies = false;
- if (freeformRemoteInputAndAction != null) {
- RemoteInput freeformRemoteInput = freeformRemoteInputAndAction.first;
- Notification.Action actionWithFreeformRemoteInput = freeformRemoteInputAndAction.second;
- hasAppGeneratedReplies = !ArrayUtils.isEmpty(freeformRemoteInput.getChoices());
- allowGeneratedReplies = actionWithFreeformRemoteInput.getAllowGeneratedReplies();
- }
-
- if (hasAppGeneratedReplies || hasAppGeneratedContextualActions) {
- return false;
- }
- return hasSmartAction && notification.getAllowSystemGeneratedContextualActions()
- || hasSmartReply && allowGeneratedReplies;
- }
-
- private void reportActionsGenerated(
- String resultId, List<ConversationAction> conversationActions) {
- if (TextUtils.isEmpty(resultId)) {
- return;
- }
- TextClassifierEvent textClassifierEvent =
- createTextClassifierEventBuilder(
- TextClassifierEvent.TYPE_ACTIONS_GENERATED, resultId)
- .setEntityTypes(conversationActions.stream()
- .map(ConversationAction::getType)
- .toArray(String[]::new))
- .build();
- getTextClassifier().onTextClassifierEvent(textClassifierEvent);
- }
-
- /**
- * Adds action adjustments based on the notification contents.
- */
- private ConversationActions suggestConversationActions(
- NotificationEntry entry,
- boolean includeReplies,
- boolean includeActions) {
- if (!includeReplies && !includeActions) {
- return EMPTY_CONVERSATION_ACTIONS;
- }
- List<ConversationActions.Message> messages = extractMessages(entry.getNotification());
- if (messages.isEmpty()) {
- return EMPTY_CONVERSATION_ACTIONS;
- }
- // Do not generate smart actions if the last message is from the local user.
- ConversationActions.Message lastMessage = messages.get(messages.size() - 1);
- if (arePersonsEqual(
- ConversationActions.Message.PERSON_USER_SELF, lastMessage.getAuthor())) {
- return EMPTY_CONVERSATION_ACTIONS;
- }
-
- TextClassifier.EntityConfig.Builder typeConfigBuilder =
- new TextClassifier.EntityConfig.Builder();
- if (!includeReplies) {
- typeConfigBuilder.setExcludedTypes(
- Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY));
- } else if (!includeActions) {
- typeConfigBuilder
- .setIncludedTypes(
- Collections.singletonList(ConversationAction.TYPE_TEXT_REPLY))
- .includeTypesFromTextClassifier(false);
- }
- ConversationActions.Request request =
- new ConversationActions.Request.Builder(messages)
- .setMaxSuggestions(mSettings.mMaxSuggestions)
- .setHints(HINTS)
- .setTypeConfig(typeConfigBuilder.build())
- .build();
- ConversationActions conversationActions =
- getTextClassifier().suggestConversationActions(request);
- reportActionsGenerated(
- conversationActions.getId(), conversationActions.getConversationActions());
- return conversationActions;
- }
-
- void onNotificationExpansionChanged(NotificationEntry entry, boolean isExpanded) {
- if (!isExpanded) {
- return;
- }
- Session session = mSessionCache.get(entry.getSbn().getKey());
- if (session == null) {
- return;
- }
- // Only report if this is the first time the user sees these suggestions.
- if (entry.isShowActionEventLogged()) {
- return;
- }
- entry.setShowActionEventLogged();
- TextClassifierEvent textClassifierEvent =
- createTextClassifierEventBuilder(
- TextClassifierEvent.TYPE_ACTIONS_SHOWN, session.resultId)
- .build();
- // TODO: If possible, report which replies / actions are actually seen by user.
- getTextClassifier().onTextClassifierEvent(textClassifierEvent);
- }
-
- void onNotificationDirectReplied(String key) {
- Session session = mSessionCache.get(key);
- if (session == null) {
- return;
- }
- TextClassifierEvent textClassifierEvent =
- createTextClassifierEventBuilder(
- TextClassifierEvent.TYPE_MANUAL_REPLY, session.resultId)
- .build();
- getTextClassifier().onTextClassifierEvent(textClassifierEvent);
- }
-
- void onSuggestedReplySent(String key, CharSequence reply,
- @NotificationAssistantService.Source int source) {
- if (source != NotificationAssistantService.SOURCE_FROM_ASSISTANT) {
- return;
- }
- Session session = mSessionCache.get(key);
- if (session == null) {
- return;
- }
- TextClassifierEvent textClassifierEvent =
- createTextClassifierEventBuilder(
- TextClassifierEvent.TYPE_SMART_ACTION, session.resultId)
- .setEntityTypes(ConversationAction.TYPE_TEXT_REPLY)
- .setScores(session.repliesScores.getOrDefault(reply, 0f))
- .build();
- getTextClassifier().onTextClassifierEvent(textClassifierEvent);
- }
-
- void onActionClicked(String key, Notification.Action action,
- @NotificationAssistantService.Source int source) {
- if (source != NotificationAssistantService.SOURCE_FROM_ASSISTANT) {
- return;
- }
- Session session = mSessionCache.get(key);
- if (session == null) {
- return;
- }
- String actionType = action.getExtras().getString(KEY_ACTION_TYPE);
- if (actionType == null) {
- return;
- }
- TextClassifierEvent textClassifierEvent =
- createTextClassifierEventBuilder(
- TextClassifierEvent.TYPE_SMART_ACTION, session.resultId)
- .setEntityTypes(actionType)
- .build();
- getTextClassifier().onTextClassifierEvent(textClassifierEvent);
- }
-
- private Notification.Action createNotificationActionFromRemoteAction(
- RemoteAction remoteAction, String actionType, float score) {
- Icon icon = remoteAction.shouldShowIcon()
- ? remoteAction.getIcon()
- : Icon.createWithResource(mContext, com.android.internal.R.drawable.ic_action_open);
- Bundle extras = new Bundle();
- extras.putString(KEY_ACTION_TYPE, actionType);
- extras.putFloat(KEY_ACTION_SCORE, score);
- return new Notification.Action.Builder(
- icon,
- remoteAction.getTitle(),
- remoteAction.getActionIntent())
- .setContextual(true)
- .addExtras(extras)
- .build();
- }
-
- private TextClassifierEvent.ConversationActionsEvent.Builder createTextClassifierEventBuilder(
- int eventType, String resultId) {
- return new TextClassifierEvent.ConversationActionsEvent.Builder(eventType)
- .setEventContext(
- new TextClassificationContext.Builder(
- mContext.getPackageName(), TextClassifier.WIDGET_TYPE_NOTIFICATION)
- .build())
- .setResultId(resultId);
- }
-
- /**
- * Returns whether a notification is eligible for action adjustments.
- *
- * <p>We exclude system notifications, those that get refreshed frequently, or ones that relate
- * to fundamental phone functionality where any error would result in a very negative user
- * experience.
- */
- private boolean isEligibleForActionAdjustment(NotificationEntry entry) {
- Notification notification = entry.getNotification();
- String pkg = entry.getSbn().getPackageName();
- if (!Process.myUserHandle().equals(entry.getSbn().getUser())) {
- return false;
- }
- if ((notification.flags & FLAG_MASK_INELGIBILE_FOR_ACTIONS) != 0) {
- return false;
- }
- if (TextUtils.isEmpty(pkg) || pkg.equals("android")) {
- return false;
- }
- // For now, we are only interested in messages.
- return entry.isMessaging();
- }
-
- private boolean isEligibleForReplyAdjustment(NotificationEntry entry) {
- if (!Process.myUserHandle().equals(entry.getSbn().getUser())) {
- return false;
- }
- String pkg = entry.getSbn().getPackageName();
- if (TextUtils.isEmpty(pkg) || pkg.equals("android")) {
- return false;
- }
- // For now, we are only interested in messages.
- if (!entry.isMessaging()) {
- return false;
- }
- // Does not make sense to provide suggested replies if it is not something that can be
- // replied.
- if (!entry.hasInlineReply()) {
- return false;
- }
- return true;
- }
-
- /** Returns the text most salient for action extraction in a notification. */
- private List<ConversationActions.Message> extractMessages(Notification notification) {
- Parcelable[] messages = notification.extras.getParcelableArray(Notification.EXTRA_MESSAGES);
- if (messages == null || messages.length == 0) {
- return Collections.singletonList(new ConversationActions.Message.Builder(
- ConversationActions.Message.PERSON_USER_OTHERS)
- .setText(notification.extras.getCharSequence(Notification.EXTRA_TEXT))
- .build());
- }
- Person localUser = notification.extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
- Deque<ConversationActions.Message> extractMessages = new ArrayDeque<>();
- for (int i = messages.length - 1; i >= 0; i--) {
- Notification.MessagingStyle.Message message =
- Notification.MessagingStyle.Message.getMessageFromBundle((Bundle) messages[i]);
- if (message == null) {
- continue;
- }
- // As per the javadoc of Notification.addMessage, null means local user.
- Person senderPerson = message.getSenderPerson();
- if (senderPerson == null) {
- senderPerson = localUser;
- }
- Person author = localUser != null && arePersonsEqual(localUser, senderPerson)
- ? ConversationActions.Message.PERSON_USER_SELF : senderPerson;
- extractMessages.push(new ConversationActions.Message.Builder(author)
- .setText(message.getText())
- .setReferenceTime(
- ZonedDateTime.ofInstant(Instant.ofEpochMilli(message.getTimestamp()),
- ZoneOffset.systemDefault()))
- .build());
- if (extractMessages.size() >= mSettings.mMaxMessagesToExtract) {
- break;
- }
- }
- return new ArrayList<>(extractMessages);
- }
-
- private TextClassifier getTextClassifier() {
- return mTextClassificationManager.getTextClassifier();
- }
-
- private static boolean arePersonsEqual(Person left, Person right) {
- return Objects.equals(left.getKey(), right.getKey())
- && Objects.equals(left.getName(), right.getName())
- && Objects.equals(left.getUri(), right.getUri());
- }
-
- static class SmartSuggestions {
- public final ArrayList<CharSequence> replies;
- public final ArrayList<Notification.Action> actions;
-
- SmartSuggestions(
- ArrayList<CharSequence> replies, ArrayList<Notification.Action> actions) {
- this.replies = replies;
- this.actions = actions;
- }
- }
-
- private static class Session {
- public final String resultId;
- public final Map<CharSequence, Float> repliesScores;
-
- Session(String resultId, Map<CharSequence, Float> repliesScores) {
- this.resultId = resultId;
- this.repliesScores = repliesScores;
- }
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmsHelper.java
deleted file mode 100644
index 07be0b8..0000000
--- a/packages/ExtServices/src/android/ext/services/notification/SmsHelper.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.ext.services.notification;
-
-import static android.provider.Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL;
-
-import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.util.Log;
-
-import com.android.internal.telephony.SmsApplication;
-
-/**
- * A helper class for storing and retrieving the default SMS application.
- */
-public class SmsHelper {
- private static final String TAG = "SmsHelper";
-
- private final Context mContext;
- private ComponentName mDefaultSmsApplication;
- private BroadcastReceiver mBroadcastReceiver;
-
- SmsHelper(Context context) {
- mContext = context.getApplicationContext();
- }
-
- void initialize() {
- if (mBroadcastReceiver == null) {
- mDefaultSmsApplication = SmsApplication.getDefaultSmsApplication(mContext, false);
- mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL.equals(intent.getAction())) {
- mDefaultSmsApplication =
- SmsApplication.getDefaultSmsApplication(mContext, false);
- } else {
- Log.w(TAG, "Unknown broadcast received: " + intent.getAction());
- }
- }
- };
- mContext.registerReceiver(
- mBroadcastReceiver,
- new IntentFilter(ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL));
- }
- }
-
- void destroy() {
- if (mBroadcastReceiver != null) {
- mContext.unregisterReceiver(mBroadcastReceiver);
- mBroadcastReceiver = null;
- }
- }
-
- @Nullable
- public ComponentName getDefaultSmsApplication() {
- return mDefaultSmsApplication;
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/resolver/LRResolverRankerService.java b/packages/ExtServices/src/android/ext/services/resolver/LRResolverRankerService.java
deleted file mode 100644
index 9d7a568..0000000
--- a/packages/ExtServices/src/android/ext/services/resolver/LRResolverRankerService.java
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.ext.services.resolver;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Environment;
-import android.os.IBinder;
-import android.os.storage.StorageManager;
-import android.service.resolver.ResolverRankerService;
-import android.service.resolver.ResolverTarget;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import java.io.File;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A Logistic Regression based {@link android.service.resolver.ResolverRankerService}, to be used
- * in {@link ResolverComparator}.
- */
-public final class LRResolverRankerService extends ResolverRankerService {
- private static final String TAG = "LRResolverRankerService";
-
- private static final boolean DEBUG = false;
-
- private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params";
- private static final String BIAS_PREF_KEY = "bias";
- private static final String VERSION_PREF_KEY = "version";
-
- private static final String LAUNCH_SCORE = "launch";
- private static final String TIME_SPENT_SCORE = "timeSpent";
- private static final String RECENCY_SCORE = "recency";
- private static final String CHOOSER_SCORE = "chooser";
-
- // parameters for a pre-trained model, to initialize the app ranker. When updating the
- // pre-trained model, please update these params, as well as initModel().
- private static final int CURRENT_VERSION = 1;
- private static final float LEARNING_RATE = 0.0001f;
- private static final float REGULARIZER_PARAM = 0.0001f;
-
- private SharedPreferences mParamSharedPref;
- private ArrayMap<String, Float> mFeatureWeights;
- private float mBias;
-
- @Override
- public IBinder onBind(Intent intent) {
- initModel();
- return super.onBind(intent);
- }
-
- @Override
- public void onPredictSharingProbabilities(List<ResolverTarget> targets) {
- final int size = targets.size();
- for (int i = 0; i < size; ++i) {
- ResolverTarget target = targets.get(i);
- ArrayMap<String, Float> features = getFeatures(target);
- target.setSelectProbability(predict(features));
- }
- }
-
- @Override
- public void onTrainRankingModel(List<ResolverTarget> targets, int selectedPosition) {
- final int size = targets.size();
- if (selectedPosition < 0 || selectedPosition >= size) {
- if (DEBUG) {
- Log.d(TAG, "Invalid Position of Selected App " + selectedPosition);
- }
- return;
- }
- final ArrayMap<String, Float> positive = getFeatures(targets.get(selectedPosition));
- final float positiveProbability = targets.get(selectedPosition).getSelectProbability();
- final int targetSize = targets.size();
- for (int i = 0; i < targetSize; ++i) {
- if (i == selectedPosition) {
- continue;
- }
- final ArrayMap<String, Float> negative = getFeatures(targets.get(i));
- final float negativeProbability = targets.get(i).getSelectProbability();
- if (negativeProbability > positiveProbability) {
- update(negative, negativeProbability, false);
- update(positive, positiveProbability, true);
- }
- }
- commitUpdate();
- }
-
- private void initModel() {
- mParamSharedPref = getParamSharedPref();
- mFeatureWeights = new ArrayMap<>(4);
- if (mParamSharedPref == null ||
- mParamSharedPref.getInt(VERSION_PREF_KEY, 0) < CURRENT_VERSION) {
- // Initializing the app ranker to a pre-trained model. When updating the pre-trained
- // model, please increment CURRENT_VERSION, and update LEARNING_RATE and
- // REGULARIZER_PARAM.
- mBias = -1.6568f;
- mFeatureWeights.put(LAUNCH_SCORE, 2.5543f);
- mFeatureWeights.put(TIME_SPENT_SCORE, 2.8412f);
- mFeatureWeights.put(RECENCY_SCORE, 0.269f);
- mFeatureWeights.put(CHOOSER_SCORE, 4.2222f);
- } else {
- mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f);
- mFeatureWeights.put(LAUNCH_SCORE, mParamSharedPref.getFloat(LAUNCH_SCORE, 0.0f));
- mFeatureWeights.put(
- TIME_SPENT_SCORE, mParamSharedPref.getFloat(TIME_SPENT_SCORE, 0.0f));
- mFeatureWeights.put(RECENCY_SCORE, mParamSharedPref.getFloat(RECENCY_SCORE, 0.0f));
- mFeatureWeights.put(CHOOSER_SCORE, mParamSharedPref.getFloat(CHOOSER_SCORE, 0.0f));
- }
- }
-
- private ArrayMap<String, Float> getFeatures(ResolverTarget target) {
- ArrayMap<String, Float> features = new ArrayMap<>(4);
- features.put(RECENCY_SCORE, target.getRecencyScore());
- features.put(TIME_SPENT_SCORE, target.getTimeSpentScore());
- features.put(LAUNCH_SCORE, target.getLaunchScore());
- features.put(CHOOSER_SCORE, target.getChooserScore());
- return features;
- }
-
- private float predict(ArrayMap<String, Float> target) {
- if (target == null) {
- return 0.0f;
- }
- final int featureSize = target.size();
- float sum = 0.0f;
- for (int i = 0; i < featureSize; i++) {
- String featureName = target.keyAt(i);
- float weight = mFeatureWeights.getOrDefault(featureName, 0.0f);
- sum += weight * target.valueAt(i);
- }
- return (float) (1.0 / (1.0 + Math.exp(-mBias - sum)));
- }
-
- private void update(ArrayMap<String, Float> target, float predict, boolean isSelected) {
- if (target == null) {
- return;
- }
- final int featureSize = target.size();
- float error = isSelected ? 1.0f - predict : -predict;
- for (int i = 0; i < featureSize; i++) {
- String featureName = target.keyAt(i);
- float currentWeight = mFeatureWeights.getOrDefault(featureName, 0.0f);
- mBias += LEARNING_RATE * error;
- currentWeight = currentWeight - LEARNING_RATE * REGULARIZER_PARAM * currentWeight +
- LEARNING_RATE * error * target.valueAt(i);
- mFeatureWeights.put(featureName, currentWeight);
- }
- if (DEBUG) {
- Log.d(TAG, "Weights: " + mFeatureWeights + " Bias: " + mBias);
- }
- }
-
- private void commitUpdate() {
- try {
- SharedPreferences.Editor editor = mParamSharedPref.edit();
- editor.putFloat(BIAS_PREF_KEY, mBias);
- final int size = mFeatureWeights.size();
- for (int i = 0; i < size; i++) {
- editor.putFloat(mFeatureWeights.keyAt(i), mFeatureWeights.valueAt(i));
- }
- editor.putInt(VERSION_PREF_KEY, CURRENT_VERSION);
- editor.apply();
- } catch (Exception e) {
- Log.e(TAG, "Failed to commit update" + e);
- }
- }
-
- private SharedPreferences getParamSharedPref() {
- // The package info in the context isn't initialized in the way it is for normal apps,
- // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
- // build the path manually below using the same policy that appears in ContextImpl.
- if (DEBUG) {
- Log.d(TAG, "Context Package Name: " + getPackageName());
- }
- final File prefsFile = new File(new File(
- Environment.getDataUserCePackageDirectory(
- StorageManager.UUID_PRIVATE_INTERNAL, getUserId(), getPackageName()),
- "shared_prefs"),
- PARAM_SHARED_PREF_NAME + ".xml");
- return getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
- }
-}
\ No newline at end of file
diff --git a/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java b/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
deleted file mode 100644
index 81a63dd..0000000
--- a/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
+++ /dev/null
@@ -1,37 +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 android.ext.services.sms;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.database.CursorWindow;
-import android.os.Bundle;
-import android.service.sms.FinancialSmsService;
-
-/**
- * Service to provide financial apps access to sms messages.
- */
-public class FinancialSmsServiceImpl extends FinancialSmsService {
-
- private static final String TAG = "FinancialSmsServiceImpl";
- private static final String KEY_COLUMN_NAMES = "column_names";
-
- @Nullable
- @Override
- public CursorWindow onGetSmsMessages(@NonNull Bundle params) {
- return null;
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/storage/CacheQuotaServiceImpl.java b/packages/ExtServices/src/android/ext/services/storage/CacheQuotaServiceImpl.java
deleted file mode 100644
index 862f50b2..0000000
--- a/packages/ExtServices/src/android/ext/services/storage/CacheQuotaServiceImpl.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.ext.services.storage;
-
-import android.app.usage.CacheQuotaHint;
-import android.app.usage.CacheQuotaService;
-import android.os.Environment;
-import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
-import android.util.ArrayMap;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-/**
- * CacheQuotaServiceImpl implements the CacheQuotaService with a strategy for populating the quota
- * of {@link CacheQuotaHint}.
- */
-public class CacheQuotaServiceImpl extends CacheQuotaService {
- private static final double CACHE_RESERVE_RATIO = 0.15;
-
- @Override
- public List<CacheQuotaHint> onComputeCacheQuotaHints(List<CacheQuotaHint> requests) {
- ArrayMap<String, List<CacheQuotaHint>> byUuid = new ArrayMap<>();
- final int requestCount = requests.size();
- for (int i = 0; i < requestCount; i++) {
- CacheQuotaHint request = requests.get(i);
- String uuid = request.getVolumeUuid();
- List<CacheQuotaHint> listForUuid = byUuid.get(uuid);
- if (listForUuid == null) {
- listForUuid = new ArrayList<>();
- byUuid.put(uuid, listForUuid);
- }
- listForUuid.add(request);
- }
-
- List<CacheQuotaHint> processed = new ArrayList<>();
- byUuid.entrySet().forEach(
- requestListEntry -> {
- // Collapse all usage stats to the same uid.
- Map<Integer, List<CacheQuotaHint>> byUid = requestListEntry.getValue()
- .stream()
- .collect(Collectors.groupingBy(CacheQuotaHint::getUid));
- byUid.values().forEach(uidGroupedList -> {
- int size = uidGroupedList.size();
- if (size < 2) {
- return;
- }
- CacheQuotaHint first = uidGroupedList.get(0);
- for (int i = 1; i < size; i++) {
- /* Note: We can't use the UsageStats built-in addition function because
- UIDs may span multiple packages and usage stats adding has
- matching package names as a precondition. */
- first.getUsageStats().mTotalTimeInForeground +=
- uidGroupedList.get(i).getUsageStats().mTotalTimeInForeground;
- }
- });
-
- // Because the foreground stats have been added to the first element, we need
- // a list of only the first values (which contain the merged foreground time).
- List<CacheQuotaHint> flattenedRequests =
- byUid.values()
- .stream()
- .map(entryList -> entryList.get(0))
- .filter(entry -> entry.getUsageStats().mTotalTimeInForeground != 0)
- .sorted(sCacheQuotaRequestComparator)
- .collect(Collectors.toList());
-
- // Because the elements are sorted, we can use the index to also be the sorted
- // index for cache quota calculation.
- double sum = getSumOfFairShares(flattenedRequests.size());
- String uuid = requestListEntry.getKey();
- long reservedSize = getReservedCacheSize(uuid);
- for (int count = 0; count < flattenedRequests.size(); count++) {
- double share = getFairShareForPosition(count) / sum;
- CacheQuotaHint entry = flattenedRequests.get(count);
- CacheQuotaHint.Builder builder = new CacheQuotaHint.Builder(entry);
- builder.setQuota(Math.round(share * reservedSize));
- processed.add(builder.build());
- }
- }
- );
-
- return processed.stream()
- .filter(request -> request.getQuota() > 0).collect(Collectors.toList());
- }
-
- private double getFairShareForPosition(int position) {
- double value = 1.0 / Math.log(position + 3) - 0.285;
- return (value > 0.01) ? value : 0.01;
- }
-
- private double getSumOfFairShares(int size) {
- double sum = 0;
- for (int i = 0; i < size; i++) {
- sum += getFairShareForPosition(i);
- }
- return sum;
- }
-
- private long getReservedCacheSize(String uuid) {
- // TODO: Revisit the cache size after running more storage tests.
- // TODO: Figure out how to ensure ExtServices has the permissions to call
- // StorageStatsManager, because this is ignoring the cache...
- StorageManager storageManager = getSystemService(StorageManager.class);
- long freeBytes = 0;
- if (uuid == StorageManager.UUID_PRIVATE_INTERNAL) { // regular equals because of null
- freeBytes = Environment.getDataDirectory().getUsableSpace();
- } else {
- final VolumeInfo vol = storageManager.findVolumeByUuid(uuid);
- freeBytes = vol.getPath().getUsableSpace();
- }
- return Math.round(freeBytes * CACHE_RESERVE_RATIO);
- }
-
- // Compares based upon foreground time.
- private static Comparator<CacheQuotaHint> sCacheQuotaRequestComparator =
- new Comparator<CacheQuotaHint>() {
- @Override
- public int compare(CacheQuotaHint o, CacheQuotaHint t1) {
- long x = t1.getUsageStats().getTotalTimeInForeground();
- long y = o.getUsageStats().getTotalTimeInForeground();
- return (x < y) ? -1 : ((x == y) ? 0 : 1);
- }
- };
-}
diff --git a/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImpl.java b/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImpl.java
deleted file mode 100644
index 670b419..0000000
--- a/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImpl.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.ext.services.watchdog;
-
-import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.provider.DeviceConfig;
-import android.service.watchdog.ExplicitHealthCheckService;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Routes explicit health check requests to the appropriate {@link ExplicitHealthChecker}.
- */
-public final class ExplicitHealthCheckServiceImpl extends ExplicitHealthCheckService {
- private static final String TAG = "ExplicitHealthCheckServiceImpl";
- // TODO: Add build dependency on NetworkStack stable AIDL so we can stop hard coding class name
- private static final String NETWORK_STACK_CONNECTOR_CLASS =
- "android.net.INetworkStackConnector";
- public static final String PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS =
- "watchdog_request_timeout_millis";
- public static final long DEFAULT_REQUEST_TIMEOUT_MILLIS =
- TimeUnit.HOURS.toMillis(1);
- // Modified only #onCreate, using concurrent collection to ensure thread visibility
- private final Map<String, ExplicitHealthChecker> mSupportedCheckers = new ConcurrentHashMap<>();
-
- @Override
- public void onCreate() {
- super.onCreate();
- initHealthCheckers();
- }
-
- @Override
- public void onRequestHealthCheck(String packageName) {
- ExplicitHealthChecker checker = mSupportedCheckers.get(packageName);
- if (checker != null) {
- checker.request();
- } else {
- Log.w(TAG, "Ignoring request for explicit health check for unsupported package "
- + packageName);
- }
- }
-
- @Override
- public void onCancelHealthCheck(String packageName) {
- ExplicitHealthChecker checker = mSupportedCheckers.get(packageName);
- if (checker != null) {
- checker.cancel();
- } else {
- Log.w(TAG, "Ignoring request to cancel explicit health check for unsupported package "
- + packageName);
- }
- }
-
- @Override
- public List<PackageConfig> onGetSupportedPackages() {
- List<PackageConfig> packages = new ArrayList<>();
- long requestTimeoutMillis = DeviceConfig.getLong(
- DeviceConfig.NAMESPACE_ROLLBACK,
- PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS,
- DEFAULT_REQUEST_TIMEOUT_MILLIS);
- if (requestTimeoutMillis <= 0) {
- requestTimeoutMillis = DEFAULT_REQUEST_TIMEOUT_MILLIS;
- }
- for (ExplicitHealthChecker checker : mSupportedCheckers.values()) {
- PackageConfig pkg = new PackageConfig(checker.getSupportedPackageName(),
- requestTimeoutMillis);
- packages.add(pkg);
- }
- return packages;
- }
-
- @Override
- public List<String> onGetRequestedPackages() {
- List<String> packages = new ArrayList<>();
- Iterator<ExplicitHealthChecker> it = mSupportedCheckers.values().iterator();
- // Could potentially race, where we read a checker#isPending and it changes before we
- // return list. However, if it races and it is in the list, the caller might call #cancel
- // which would fail, but that is fine. If it races and it ends up *not* in the list, it was
- // already cancelled, so there's no need for the caller to cancel it
- while (it.hasNext()) {
- ExplicitHealthChecker checker = it.next();
- if (checker.isPending()) {
- packages.add(checker.getSupportedPackageName());
- }
- }
- return packages;
- }
-
- private void initHealthCheckers() {
- Intent intent = new Intent(NETWORK_STACK_CONNECTOR_CLASS);
- ComponentName comp = intent.resolveSystemService(getPackageManager(), 0);
- if (comp != null) {
- String networkStackPackageName = comp.getPackageName();
- mSupportedCheckers.put(networkStackPackageName,
- new NetworkChecker(this, networkStackPackageName));
- } else {
- // On Go devices, or any device that does not ship the network stack module.
- // The network stack will live in system_server process, so no need to monitor.
- Log.i(TAG, "Network stack module not found");
- }
- }
-}
diff --git a/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthChecker.java b/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthChecker.java
deleted file mode 100644
index a982d52..0000000
--- a/packages/ExtServices/src/android/ext/services/watchdog/ExplicitHealthChecker.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.ext.services.watchdog;
-
-/**
- * A type of explicit health check that can be performed on a device, e.g network health check
- */
-interface ExplicitHealthChecker {
- /**
- * Requests a checker to listen to explicit health checks for {@link #getPackageName}.
- * {@link #isPending} will now return {@code true}.
- */
- void request();
-
- /**
- * Cancels a pending explicit health check request for {@link #getPackageName}.
- * {@link #isPending} will now return {@code false}.
- */
- void cancel();
-
- /**
- * Returns {@code true} if a request is pending, {@code false} otherwise.
- */
- boolean isPending();
-
- /**
- * Returns the name of the package this checker can make requests for.
- */
- String getSupportedPackageName();
-}
diff --git a/packages/ExtServices/src/android/ext/services/watchdog/NetworkChecker.java b/packages/ExtServices/src/android/ext/services/watchdog/NetworkChecker.java
deleted file mode 100644
index 5722e09..0000000
--- a/packages/ExtServices/src/android/ext/services/watchdog/NetworkChecker.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.ext.services.watchdog;
-
-import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-import android.service.watchdog.ExplicitHealthCheckService;
-
-import com.android.internal.annotations.GuardedBy;
-
-/**
- * Observes the network stack via the ConnectivityManager.
- */
-final class NetworkChecker extends ConnectivityManager.NetworkCallback
- implements ExplicitHealthChecker {
- private static final String TAG = "NetworkChecker";
-
- private final Object mLock = new Object();
- private final ExplicitHealthCheckService mService;
- private final String mPackageName;
- @GuardedBy("mLock")
- private boolean mIsPending;
-
- NetworkChecker(ExplicitHealthCheckService service, String packageName) {
- mService = service;
- mPackageName = packageName;
- }
-
- @Override
- public void request() {
- synchronized (mLock) {
- if (mIsPending) {
- return;
- }
- mService.getSystemService(ConnectivityManager.class).registerNetworkCallback(
- new NetworkRequest.Builder().build(), this);
- mIsPending = true;
- }
- }
-
- @Override
- public void cancel() {
- synchronized (mLock) {
- if (!mIsPending) {
- return;
- }
- mService.getSystemService(ConnectivityManager.class).unregisterNetworkCallback(this);
- mIsPending = false;
- }
- }
-
- @Override
- public boolean isPending() {
- synchronized (mLock) {
- return mIsPending;
- }
- }
-
- @Override
- public String getSupportedPackageName() {
- return mPackageName;
- }
-
- // TODO(b/120598832): Also monitor NetworkCallback#onAvailable to see if we have any
- // available networks that may be unusable. This could be additional signal to our heuristics
- @Override
- public void onCapabilitiesChanged(Network network, NetworkCapabilities capabilities) {
- synchronized (mLock) {
- if (mIsPending
- && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
- mService.notifyHealthCheckPassed(mPackageName);
- cancel();
- }
- }
- }
-}
diff --git a/packages/ExtServices/tests/Android.bp b/packages/ExtServices/tests/Android.bp
deleted file mode 100644
index ae60d4e..0000000
--- a/packages/ExtServices/tests/Android.bp
+++ /dev/null
@@ -1,27 +0,0 @@
-android_test {
- name: "ExtServicesUnitTests",
-
- // Include all test java files.
- srcs: ["src/**/*.java"],
-
- // We only want this apk build for tests.
- certificate: "platform",
-
- libs: [
- "android.test.runner",
- "android.test.base",
- ],
-
- static_libs: [
- "ExtServices-core",
- "androidx.test.rules",
- "compatibility-device-util-axt",
- "mockito-target-minus-junit4",
- "androidx.test.espresso.core",
- "truth-prebuilt",
- "testables",
- "testng",
- ],
-
- platform_apis: true,
-}
diff --git a/packages/ExtServices/tests/AndroidManifest.xml b/packages/ExtServices/tests/AndroidManifest.xml
deleted file mode 100644
index a08a10e..0000000
--- a/packages/ExtServices/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.ext.services.tests.unit">
-
- <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
-
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.ext.services.tests.unit"
- android:label="ExtServices Test Cases">
- </instrumentation>
-
-</manifest>
\ No newline at end of file
diff --git a/packages/ExtServices/tests/AndroidTest.xml b/packages/ExtServices/tests/AndroidTest.xml
deleted file mode 100644
index cd26ebc..0000000
--- a/packages/ExtServices/tests/AndroidTest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
--->
-<configuration description="Runs Tests for ExtServices">
- <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
- <option name="test-file-name" value="ExtServicesUnitTests.apk" />
- </target_preparer>
-
- <option name="test-suite-tag" value="apct" />
- <option name="test-suite-tag" value="framework-base-presubmit" />
- <option name="test-tag" value="ExtServicesUnitTests" />
- <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="android.ext.services.tests.unit" />
- <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
- <option name="hidden-api-checks" value="false"/>
- </test>
-</configuration>
\ No newline at end of file
diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java
deleted file mode 100644
index 6fda4c7..0000000
--- a/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java
+++ /dev/null
@@ -1,118 +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 android.ext.services.autofill;
-
-import static android.service.autofill.AutofillFieldClassificationService.REQUIRED_ALGORITHM_EXACT_MATCH;
-import static android.view.autofill.AutofillValue.forText;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.os.Bundle;
-import android.view.autofill.AutofillValue;
-
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Contains the base tests that does not rely on the specific algorithm implementation.
- */
-public class AutofillFieldClassificationServiceImplTest {
-
- private final AutofillFieldClassificationServiceImpl mService =
- new AutofillFieldClassificationServiceImpl();
-
- @Test
- public void testOnCalculateScores_nullActualValues() {
- assertThat(mService.onCalculateScores(null, null, null, null, null, null, null)).isNull();
- }
-
- @Test
- public void testOnCalculateScores_emptyActualValues() {
- assertThat(mService.onCalculateScores(Collections.emptyList(), Arrays.asList("whatever"),
- null, null, null, null, null)).isNull();
- }
-
- @Test
- public void testOnCalculateScores_nullUserDataValues() {
- assertThat(mService.onCalculateScores(Arrays.asList(AutofillValue.forText("whatever")),
- null, null, null, null, null, null)).isNull();
- }
-
- @Test
- public void testOnCalculateScores_emptyUserDataValues() {
- assertThat(mService.onCalculateScores(Arrays.asList(AutofillValue.forText("whatever")),
- Collections.emptyList(), null, null, null, null, null))
- .isNull();
- }
-
- @Test
- public void testCalculateScores() {
- final List<AutofillValue> actualValues = Arrays.asList(forText("A"), forText("b"),
- forText("dude"));
- final List<String> userDataValues = Arrays.asList("a", "b", "B", "ab", "c", "dude",
- "sweet_dude", "dude_sweet");
- final List<String> categoryIds = Arrays.asList("cat", "cat", "cat", "cat", "cat", "last4",
- "last4", "last4");
- final HashMap<String, String> algorithms = new HashMap<>(1);
- algorithms.put("last4", REQUIRED_ALGORITHM_EXACT_MATCH);
-
- final Bundle last4Bundle = new Bundle();
- last4Bundle.putInt("suffix", 4);
-
- final HashMap<String, Bundle> args = new HashMap<>(1);
- args.put("last4", last4Bundle);
-
- final float[][] expectedScores = new float[][] {
- new float[] { 1F, 0F, 0F, 0.5F, 0F, 0F, 0F, 0F },
- new float[] { 0F, 1F, 1F, 0.5F, 0F, 0F, 0F, 0F },
- new float[] { 0F, 0F, 0F, 0F , 0F, 1F, 1F, 0F }
- };
- final float[][] actualScores = mService.onCalculateScores(actualValues, userDataValues,
- categoryIds, null, null, algorithms, args);
-
- // Unfortunately, Truth does not have an easy way to compare float matrices and show useful
- // messages in case of error, so we need to check.
- assertWithMessage("actual=%s, expected=%s", toString(actualScores),
- toString(expectedScores)).that(actualScores.length).isEqualTo(3);
- for (int i = 0; i < 3; i++) {
- assertWithMessage("actual=%s, expected=%s", toString(actualScores),
- toString(expectedScores)).that(actualScores[i].length).isEqualTo(8);
- }
-
- for (int i = 0; i < actualScores.length; i++) {
- final float[] line = actualScores[i];
- for (int j = 0; j < line.length; j++) {
- float cell = line[j];
- assertWithMessage("wrong score at [%s, %s]", i, j).that(cell).isWithin(0.01F)
- .of(expectedScores[i][j]);
- }
- }
- }
-
- public static String toString(float[][] matrix) {
- final StringBuilder string = new StringBuilder("[ ");
- for (int i = 0; i < matrix.length; i++) {
- string.append(Arrays.toString(matrix[i])).append(" ");
- }
- return string.append(" ]").toString();
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java
deleted file mode 100644
index 3d754f7..0000000
--- a/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java
+++ /dev/null
@@ -1,85 +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 android.ext.services.autofill;
-
-import static android.ext.services.autofill.EditDistanceScorer.calculateScore;
-import static android.ext.services.autofill.EditDistanceScorer.editDistance;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.view.autofill.AutofillValue;
-
-import org.junit.Test;
-
-public class EditDistanceScorerTest {
-
- @Test
- public void testCalculateScore_nullValue() {
- assertFloat(calculateScore(null, "D'OH!"), 0);
- }
-
- @Test
- public void testCalculateScore_nonTextValue() {
- assertFloat(calculateScore(AutofillValue.forToggle(true), "D'OH!"), 0);
- }
-
- @Test
- public void testCalculateScore_nullUserData() {
- assertFloat(calculateScore(AutofillValue.forText("D'OH!"), null), 0);
- }
-
- @Test
- public void testCalculateScore_fullMatch() {
- assertFloat(calculateScore(AutofillValue.forText("D'OH!"), "D'OH!"), 1);
- assertFloat(calculateScore(AutofillValue.forText(""), ""), 1);
- }
-
- @Test
- public void testCalculateScore_fullMatchMixedCase() {
- assertFloat(calculateScore(AutofillValue.forText("D'OH!"), "D'oH!"), 1);
- }
-
- @Test
- public void testCalculateScore_mismatchDifferentSizes() {
- assertFloat(calculateScore(AutofillValue.forText("X"), "Xy"), 0.50F);
- assertFloat(calculateScore(AutofillValue.forText("Xy"), "X"), 0.50F);
- assertFloat(calculateScore(AutofillValue.forText("One"), "MoreThanOne"), 0.27F);
- assertFloat(calculateScore(AutofillValue.forText("MoreThanOne"), "One"), 0.27F);
- assertFloat(calculateScore(AutofillValue.forText("1600 Amphitheatre Parkway"),
- "1600 Amphitheatre Pkwy"), 0.88F);
- assertFloat(calculateScore(AutofillValue.forText("1600 Amphitheatre Pkwy"),
- "1600 Amphitheatre Parkway"), 0.88F);
- }
-
- @Test
- public void testCalculateScore_partialMatch() {
- assertFloat(calculateScore(AutofillValue.forText("Dude"), "Dxxx"), 0.25F);
- assertFloat(calculateScore(AutofillValue.forText("Dude"), "DUxx"), 0.50F);
- assertFloat(calculateScore(AutofillValue.forText("Dude"), "DUDx"), 0.75F);
- assertFloat(calculateScore(AutofillValue.forText("Dxxx"), "Dude"), 0.25F);
- assertFloat(calculateScore(AutofillValue.forText("DUxx"), "Dude"), 0.50F);
- assertFloat(calculateScore(AutofillValue.forText("DUDx"), "Dude"), 0.75F);
- }
-
- @Test
- public void testEditDistance_maxDistance() {
- assertFloat(editDistance("testing", "b", 4), Integer.MAX_VALUE);
- }
-
- public static void assertFloat(float actualValue, float expectedValue) {
- assertThat(actualValue).isWithin(0.01F).of(expectedValue);
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/ExactMatchTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/ExactMatchTest.java
deleted file mode 100644
index bf5e160..0000000
--- a/packages/ExtServices/tests/src/android/ext/services/autofill/ExactMatchTest.java
+++ /dev/null
@@ -1,98 +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 android.ext.services.autofill;
-
-import static android.ext.services.autofill.ExactMatch.calculateScore;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.os.Bundle;
-import android.view.autofill.AutofillValue;
-
-import org.junit.Test;
-
-public class ExactMatchTest {
-
- private Bundle last4Bundle() {
- final Bundle bundle = new Bundle();
- bundle.putInt("suffix", 4);
- return bundle;
- }
-
- @Test
- public void testCalculateScore_nullValue() {
- assertFloat(calculateScore(null, "TEST", null), 0);
- }
-
- @Test
- public void testCalculateScore_nonTextValue() {
- assertFloat(calculateScore(AutofillValue.forToggle(true), "TEST", null), 0);
- }
-
- @Test
- public void testCalculateScore_nullUserData() {
- assertFloat(calculateScore(AutofillValue.forText("TEST"), null, null), 0);
- }
-
- @Test
- public void testCalculateScore_succeedMatchMixedCases_last4() {
- final Bundle last4 = last4Bundle();
- assertFloat(calculateScore(AutofillValue.forText("TEST"), "1234 test", last4), 1);
- assertFloat(calculateScore(AutofillValue.forText("test"), "1234 TEST", last4), 1);
- }
-
- @Test
- public void testCalculateScore_mismatchDifferentSizes_last4() {
- final Bundle last4 = last4Bundle();
- assertFloat(calculateScore(AutofillValue.forText("TEST"), "TEST1", last4), 0);
- assertFloat(calculateScore(AutofillValue.forText(""), "TEST", last4), 0);
- assertFloat(calculateScore(AutofillValue.forText("TEST"), "", last4), 0);
- }
-
- @Test
- public void testCalculateScore_match() {
- final Bundle last4 = last4Bundle();
- assertFloat(calculateScore(AutofillValue.forText("1234 1234 1234 1234"),
- "xxxx xxxx xxxx 1234", last4), 1);
- assertFloat(calculateScore(AutofillValue.forText("TEST"), "TEST", null), 1);
- assertFloat(calculateScore(AutofillValue.forText("TEST 1234"), "1234", last4), 1);
- assertFloat(calculateScore(AutofillValue.forText("TEST"), "test", null), 1);
- }
-
- @Test
- public void testCalculateScore_badBundle() {
- final Bundle bundle = new Bundle();
- bundle.putInt("suffix", -2);
- assertThrows(IllegalArgumentException.class, () -> calculateScore(
- AutofillValue.forText("TEST"), "TEST", bundle));
-
- final Bundle largeBundle = new Bundle();
- largeBundle.putInt("suffix", 10);
- assertFloat(calculateScore(AutofillValue.forText("TEST"), "TEST", largeBundle), 1);
-
- final Bundle stringBundle = new Bundle();
- stringBundle.putString("suffix", "value");
- assertThrows(IllegalArgumentException.class, () -> calculateScore(
- AutofillValue.forText("TEST"), "TEST", stringBundle));
-
- }
-
- public static void assertFloat(float actualValue, float expectedValue) {
- assertThat(actualValue).isWithin(0.01F).of(expectedValue);
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java
deleted file mode 100644
index a87d57c..0000000
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AgingHelperTest.java
+++ /dev/null
@@ -1,160 +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 android.ext.services.notification;
-
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.AlarmManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.PendingIntent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.os.Build;
-import android.os.Process;
-import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
-import android.testing.TestableContext;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(AndroidJUnit4.class)
-public class AgingHelperTest {
- private String mPkg = "pkg";
- private int mUid = 2018;
-
- @Rule
- public final TestableContext mContext =
- new TestableContext(InstrumentationRegistry.getTargetContext(), null);
-
- @Mock
- private NotificationCategorizer mCategorizer;
- @Mock
- private AlarmManager mAlarmManager;
- @Mock
- private IPackageManager mPackageManager;
- @Mock
- private AgingHelper.Callback mCallback;
- @Mock
- private SmsHelper mSmsHelper;
-
- private AgingHelper mAgingHelper;
-
- private StatusBarNotification generateSbn(String channelId) {
- Notification n = new Notification.Builder(mContext, channelId)
- .setContentTitle("foo")
- .build();
-
- return new StatusBarNotification(mPkg, mPkg, 0, "tag", mUid, mUid, n,
- UserHandle.SYSTEM, null, 0);
- }
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mPkg = mContext.getPackageName();
- mUid = Process.myUid();
-
- ApplicationInfo info = mock(ApplicationInfo.class);
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt()))
- .thenReturn(info);
- info.targetSdkVersion = Build.VERSION_CODES.P;
-
- mContext.addMockSystemService(AlarmManager.class, mAlarmManager);
-
- mAgingHelper = new AgingHelper(mContext, mCategorizer, mCallback);
- }
-
- @Test
- public void testNoSnoozingOnPost() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- StatusBarNotification sbn = generateSbn(channel.getId());
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, sbn, channel, mSmsHelper);
-
-
- mAgingHelper.onNotificationPosted(entry);
- verify(mAlarmManager, never()).setExactAndAllowWhileIdle(anyInt(), anyLong(), any());
- }
-
- @Test
- public void testPostResetsSnooze() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- StatusBarNotification sbn = generateSbn(channel.getId());
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, sbn, channel, mSmsHelper);
-
-
- mAgingHelper.onNotificationPosted(entry);
- verify(mAlarmManager, times(1)).cancel(any(PendingIntent.class));
- }
-
- @Test
- public void testSnoozingOnSeen() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- StatusBarNotification sbn = generateSbn(channel.getId());
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, sbn, channel, mSmsHelper);
- entry.setSeen();
- when(mCategorizer.getCategory(entry)).thenReturn(NotificationCategorizer.CATEGORY_PEOPLE);
-
- mAgingHelper.onNotificationSeen(entry);
- verify(mAlarmManager, times(1)).setExactAndAllowWhileIdle(anyInt(), anyLong(), any());
- }
-
- @Test
- public void testNoSnoozingOnSeenUserLocked() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
- StatusBarNotification sbn = generateSbn(channel.getId());
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, sbn, channel, mSmsHelper);
- when(mCategorizer.getCategory(entry)).thenReturn(NotificationCategorizer.CATEGORY_PEOPLE);
-
- mAgingHelper.onNotificationSeen(entry);
- verify(mAlarmManager, never()).setExactAndAllowWhileIdle(anyInt(), anyLong(), any());
- }
-
- @Test
- public void testNoSnoozingOnSeenAlreadyLow() {
- NotificationEntry entry = mock(NotificationEntry.class);
- when(entry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_HIGH));
- when(entry.getImportance()).thenReturn(IMPORTANCE_MIN);
-
- mAgingHelper.onNotificationSeen(entry);
- verify(mAlarmManager, never()).setExactAndAllowWhileIdle(anyInt(), anyLong(), any());
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java
deleted file mode 100644
index 5c877de..0000000
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java
+++ /dev/null
@@ -1,263 +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 android.ext.services.notification;
-
-import static android.ext.services.notification.AssistantSettings.DEFAULT_MAX_SUGGESTIONS;
-import static android.provider.DeviceConfig.setProperty;
-
-import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.content.ContentResolver;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.DeviceConfig;
-import android.provider.Settings;
-import android.support.test.uiautomator.UiDevice;
-import android.testing.TestableContext;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.IOException;
-
-@RunWith(AndroidJUnit4.class)
-public class AssistantSettingsTest {
- private static final String CLEAR_DEVICE_CONFIG_KEY_CMD =
- "device_config delete " + DeviceConfig.NAMESPACE_SYSTEMUI;
-
- private static final int USER_ID = 5;
-
- @Rule
- public final TestableContext mContext =
- new TestableContext(InstrumentationRegistry.getContext(), null);
-
- @Mock Runnable mOnUpdateRunnable;
-
- private ContentResolver mResolver;
- private AssistantSettings mAssistantSettings;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mResolver = mContext.getContentResolver();
- Handler handler = new Handler(Looper.getMainLooper());
-
- // To bypass real calls to global settings values, set the Settings values here.
- Settings.Global.putFloat(mResolver,
- Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, 0.8f);
- Settings.Global.putInt(mResolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, 2);
- Settings.Secure.putInt(mResolver, Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
-
- mAssistantSettings = AssistantSettings.createForTesting(
- handler, mResolver, USER_ID, mOnUpdateRunnable);
- }
-
- @After
- public void tearDown() throws IOException {
- clearDeviceConfig();
- }
-
- @Test
- public void testGenerateRepliesDisabled() {
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES,
- "false",
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- assertFalse(mAssistantSettings.mGenerateReplies);
- }
-
- @Test
- public void testGenerateRepliesEnabled() {
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES,
- "true",
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- assertTrue(mAssistantSettings.mGenerateReplies);
- }
-
- @Test
- public void testGenerateRepliesNullFlag() {
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES,
- "false",
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- assertFalse(mAssistantSettings.mGenerateReplies);
-
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES,
- null,
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- // Go back to the default value.
- assertTrue(mAssistantSettings.mGenerateReplies);
- }
-
- @Test
- public void testGenerateActionsDisabled() {
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS,
- "false",
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- assertFalse(mAssistantSettings.mGenerateActions);
- }
-
- @Test
- public void testGenerateActionsEnabled() {
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS,
- "true",
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- assertTrue(mAssistantSettings.mGenerateActions);
- }
-
- @Test
- public void testGenerateActionsNullFlag() {
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS,
- "false",
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- assertFalse(mAssistantSettings.mGenerateActions);
-
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS,
- null,
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- // Go back to the default value.
- assertTrue(mAssistantSettings.mGenerateActions);
- }
-
- @Test
- public void testMaxMessagesToExtract() {
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_MAX_MESSAGES_TO_EXTRACT,
- "10",
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- assertEquals(10, mAssistantSettings.mMaxMessagesToExtract);
- }
-
- @Test
- public void testMaxSuggestions() {
- runWithShellPermissionIdentity(() -> setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NAS_MAX_SUGGESTIONS,
- "5",
- false /* makeDefault */));
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- assertEquals(5, mAssistantSettings.mMaxSuggestions);
- }
-
- @Test
- public void testMaxSuggestionsEmpty() {
- mAssistantSettings.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
-
- assertEquals(DEFAULT_MAX_SUGGESTIONS, mAssistantSettings.mMaxSuggestions);
- }
-
- @Test
- public void testStreakLimit() {
- verify(mOnUpdateRunnable, never()).run();
-
- // Update settings value.
- int newStreakLimit = 4;
- Settings.Global.putInt(mResolver,
- Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, newStreakLimit);
-
- // Notify for the settings value we updated.
- mAssistantSettings.onChange(false, Settings.Global.getUriFor(
- Settings.Global.BLOCKING_HELPER_STREAK_LIMIT));
-
- assertEquals(newStreakLimit, mAssistantSettings.mStreakLimit);
- verify(mOnUpdateRunnable).run();
- }
-
- @Test
- public void testDismissToViewRatioLimit() {
- verify(mOnUpdateRunnable, never()).run();
-
- // Update settings value.
- float newDismissToViewRatioLimit = 3f;
- Settings.Global.putFloat(mResolver,
- Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
- newDismissToViewRatioLimit);
-
- // Notify for the settings value we updated.
- mAssistantSettings.onChange(false, Settings.Global.getUriFor(
- Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT));
-
- assertEquals(newDismissToViewRatioLimit, mAssistantSettings.mDismissToViewRatioLimit, 1e-6);
- verify(mOnUpdateRunnable).run();
- }
-
- private static void clearDeviceConfig() throws IOException {
- UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- uiDevice.executeShellCommand(
- CLEAR_DEVICE_CONFIG_KEY_CMD + " " + SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS);
- uiDevice.executeShellCommand(
- CLEAR_DEVICE_CONFIG_KEY_CMD + " " + SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES);
- uiDevice.executeShellCommand(
- CLEAR_DEVICE_CONFIG_KEY_CMD + " "
- + SystemUiDeviceConfigFlags.NAS_MAX_MESSAGES_TO_EXTRACT);
- uiDevice.executeShellCommand(
- CLEAR_DEVICE_CONFIG_KEY_CMD + " " + SystemUiDeviceConfigFlags.NAS_MAX_SUGGESTIONS);
- }
-
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
deleted file mode 100644
index 012dcc0..0000000
--- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
+++ /dev/null
@@ -1,477 +0,0 @@
-/**
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.ext.services.notification;
-
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.Application;
-import android.app.INotificationManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.os.Build;
-import android.os.UserHandle;
-import android.service.notification.Adjustment;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.NotificationListenerService.RankingMap;
-import android.service.notification.NotificationStats;
-import android.service.notification.StatusBarNotification;
-import android.test.ServiceTestCase;
-import android.testing.TestableContext;
-import android.util.AtomicFile;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.android.internal.util.FastXmlSerializer;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.FileOutputStream;
-import java.util.ArrayList;
-
-public class AssistantTest extends ServiceTestCase<Assistant> {
-
- private static final String PKG1 = "pkg1";
- private static final int UID1 = 1;
- private static final NotificationChannel P1C1 =
- new NotificationChannel("one", "", IMPORTANCE_LOW);
- private static final NotificationChannel P1C2 =
- new NotificationChannel("p1c2", "", IMPORTANCE_DEFAULT);
- private static final NotificationChannel P1C3 =
- new NotificationChannel("p1c3", "", IMPORTANCE_MIN);
- private static final String PKG2 = "pkg2";
-
- private static final int UID2 = 2;
- private static final NotificationChannel P2C1 =
- new NotificationChannel("one", "", IMPORTANCE_LOW);
-
- @Mock INotificationManager mNoMan;
- @Mock AtomicFile mFile;
- @Mock IPackageManager mPackageManager;
- @Mock SmsHelper mSmsHelper;
-
- Assistant mAssistant;
- Application mApplication;
-
- @Rule
- public final TestableContext mContext =
- new TestableContext(InstrumentationRegistry.getContext(), null);
-
- public AssistantTest() {
- super(Assistant.class);
- }
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- Intent startIntent =
- new Intent("android.service.notification.NotificationAssistantService");
- startIntent.setPackage("android.ext.services");
-
- mApplication = (Application) InstrumentationRegistry.getInstrumentation().
- getTargetContext().getApplicationContext();
- // Force the test to use the correct application instead of trying to use a mock application
- setApplication(mApplication);
-
- setupService();
- mAssistant = getService();
-
- // Override the AssistantSettings factory.
- mAssistant.mSettingsFactory = AssistantSettings::createForTesting;
-
- bindService(startIntent);
-
- mAssistant.mSettings.mDismissToViewRatioLimit = 0.8f;
- mAssistant.mSettings.mStreakLimit = 2;
- mAssistant.mSettings.mNewInterruptionModel = true;
- mAssistant.setNoMan(mNoMan);
- mAssistant.setFile(mFile);
- mAssistant.setPackageManager(mPackageManager);
-
- ApplicationInfo info = mock(ApplicationInfo.class);
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt()))
- .thenReturn(info);
- info.targetSdkVersion = Build.VERSION_CODES.P;
- when(mFile.startWrite()).thenReturn(mock(FileOutputStream.class));
- }
-
- private StatusBarNotification generateSbn(String pkg, int uid, NotificationChannel channel,
- String tag, String groupKey) {
- Notification n = new Notification.Builder(mContext, channel.getId())
- .setContentTitle("foo")
- .setGroup(groupKey)
- .build();
-
- StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, 0, tag, uid, uid, n,
- UserHandle.SYSTEM, null, 0);
-
- return sbn;
- }
-
- private Ranking generateRanking(StatusBarNotification sbn, NotificationChannel channel) {
- Ranking mockRanking = mock(Ranking.class);
- when(mockRanking.getChannel()).thenReturn(channel);
- when(mockRanking.getImportance()).thenReturn(channel.getImportance());
- when(mockRanking.getKey()).thenReturn(sbn.getKey());
- when(mockRanking.getOverrideGroupKey()).thenReturn(null);
- return mockRanking;
- }
-
- private void almostBlockChannel(String pkg, int uid, NotificationChannel channel) {
- for (int i = 0; i < ChannelImpressions.DEFAULT_STREAK_LIMIT; i++) {
- dismissBadNotification(pkg, uid, channel, String.valueOf(i));
- }
- }
-
- private void dismissBadNotification(String pkg, int uid, NotificationChannel channel,
- String tag) {
- StatusBarNotification sbn = generateSbn(pkg, uid, channel, tag, null);
- mAssistant.setFakeRanking(generateRanking(sbn, channel));
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
- mAssistant.setFakeRanking(mock(Ranking.class));
- NotificationStats stats = new NotificationStats();
- stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
- stats.setSeen();
- mAssistant.onNotificationRemoved(
- sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
- }
-
- @Test
- public void testNoAdjustmentForInitialPost() throws Exception {
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, null, null);
-
- mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- verify(mNoMan, never()).applyEnqueuedAdjustmentFromAssistant(any(), any());
- }
-
- @Test
- public void testTriggerAdjustment() throws Exception {
- almostBlockChannel(PKG1, UID1, P1C1);
- dismissBadNotification(PKG1, UID1, P1C1, "trigger!");
-
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
- mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- ArgumentCaptor<Adjustment> captor = ArgumentCaptor.forClass(Adjustment.class);
- verify(mNoMan, times(1)).applyEnqueuedAdjustmentFromAssistant(any(), captor.capture());
- assertEquals(sbn.getKey(), captor.getValue().getKey());
- assertEquals(Ranking.USER_SENTIMENT_NEGATIVE,
- captor.getValue().getSignals().getInt(Adjustment.KEY_USER_SENTIMENT));
- }
-
- @Test
- public void testMinCannotTriggerAdjustment() throws Exception {
- almostBlockChannel(PKG1, UID1, P1C3);
- dismissBadNotification(PKG1, UID1, P1C3, "trigger!");
-
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C3, "new one!", null);
- mAssistant.setFakeRanking(generateRanking(sbn, P1C3));
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- verify(mNoMan, never()).applyEnqueuedAdjustmentFromAssistant(any(), any());
- }
-
- @Test
- public void testGroupChildCanTriggerAdjustment() throws Exception {
- almostBlockChannel(PKG1, UID1, P1C1);
-
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", "I HAVE A GROUP");
- mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
- NotificationStats stats = new NotificationStats();
- stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
- stats.setSeen();
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
- mAssistant.onNotificationRemoved(
- sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
-
- sbn = generateSbn(PKG1, UID1, P1C1, "new one!", "group");
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- ArgumentCaptor<Adjustment> captor = ArgumentCaptor.forClass(Adjustment.class);
- verify(mNoMan, times(1)).applyEnqueuedAdjustmentFromAssistant(any(), captor.capture());
- assertEquals(sbn.getKey(), captor.getValue().getKey());
- assertEquals(Ranking.USER_SENTIMENT_NEGATIVE,
- captor.getValue().getSignals().getInt(Adjustment.KEY_USER_SENTIMENT));
- }
-
- @Test
- public void testGroupSummaryCannotTriggerAdjustment() throws Exception {
- almostBlockChannel(PKG1, UID1, P1C1);
-
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", "I HAVE A GROUP");
- sbn.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
- mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
- NotificationStats stats = new NotificationStats();
- stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
- stats.setSeen();
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
- mAssistant.onNotificationRemoved(
- sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
-
- sbn = generateSbn(PKG1, UID1, P1C1, "new one!", "group");
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- verify(mNoMan, never()).applyEnqueuedAdjustmentFromAssistant(any(), any());
- }
-
- @Test
- public void testAodCannotTriggerAdjustment() throws Exception {
- almostBlockChannel(PKG1, UID1, P1C1);
-
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null);
- mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
- NotificationStats stats = new NotificationStats();
- stats.setDismissalSurface(NotificationStats.DISMISSAL_AOD);
- stats.setSeen();
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
- mAssistant.onNotificationRemoved(
- sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
-
- sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- verify(mNoMan, never()).applyEnqueuedAdjustmentFromAssistant(any(), any());
- }
-
- @Test
- public void testInteractedCannotTriggerAdjustment() throws Exception {
- almostBlockChannel(PKG1, UID1, P1C1);
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null);
- mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
- NotificationStats stats = new NotificationStats();
- stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
- stats.setSeen();
- stats.setExpanded();
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
- mAssistant.onNotificationRemoved(
- sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
-
- sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- verify(mNoMan, never()).applyEnqueuedAdjustmentFromAssistant(any(), any());
- }
-
- @Test
- public void testAppDismissedCannotTriggerAdjustment() throws Exception {
- almostBlockChannel(PKG1, UID1, P1C1);
-
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null);
- mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
- NotificationStats stats = new NotificationStats();
- stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
- stats.setSeen();
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
- mAssistant.onNotificationRemoved(
- sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_APP_CANCEL);
-
- sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- verify(mNoMan, never()).applyEnqueuedAdjustmentFromAssistant(any(), any());
- }
-
- @Test
- public void testAppSeparation() throws Exception {
- almostBlockChannel(PKG1, UID1, P1C1);
- dismissBadNotification(PKG1, UID1, P1C1, "trigger!");
-
- StatusBarNotification sbn = generateSbn(PKG2, UID2, P2C1, "new app!", null);
- mAssistant.setFakeRanking(generateRanking(sbn, P2C1));
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- verify(mNoMan, never()).applyEnqueuedAdjustmentFromAssistant(any(), any());
- }
-
- @Test
- public void testChannelSeparation() throws Exception {
- almostBlockChannel(PKG1, UID1, P1C1);
- dismissBadNotification(PKG1, UID1, P1C1, "trigger!");
-
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C2, "new app!", null);
- mAssistant.setFakeRanking(generateRanking(sbn, P1C2));
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- verify(mNoMan, never()).applyEnqueuedAdjustmentFromAssistant(any(), any());
- }
-
- @Test
- public void testReadXml() throws Exception {
- String key1 = mAssistant.getKey("pkg1", 1, "channel1");
- int streak1 = 2;
- int views1 = 5;
- int dismiss1 = 9;
-
- int streak1a = 3;
- int views1a = 10;
- int dismiss1a = 99;
- String key1a = mAssistant.getKey("pkg1", 1, "channel1a");
-
- int streak2 = 7;
- int views2 = 77;
- int dismiss2 = 777;
- String key2 = mAssistant.getKey("pkg2", 2, "channel2");
-
- String xml = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
- + "<assistant version=\"1\">\n"
- + "<impression-set key=\"" + key1 + "\" "
- + "dismisses=\"" + dismiss1 + "\" views=\"" + views1
- + "\" streak=\"" + streak1 + "\"/>\n"
- + "<impression-set key=\"" + key1a + "\" "
- + "dismisses=\"" + dismiss1a + "\" views=\"" + views1a
- + "\" streak=\"" + streak1a + "\"/>\n"
- + "<impression-set key=\"" + key2 + "\" "
- + "dismisses=\"" + dismiss2 + "\" views=\"" + views2
- + "\" streak=\"" + streak2 + "\"/>\n"
- + "</assistant>\n";
- mAssistant.readXml(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())));
-
- ChannelImpressions c1 = mAssistant.getImpressions(key1);
- assertEquals(2, c1.getStreak());
- assertEquals(5, c1.getViews());
- assertEquals(9, c1.getDismissals());
-
- ChannelImpressions c1a = mAssistant.getImpressions(key1a);
- assertEquals(3, c1a.getStreak());
- assertEquals(10, c1a.getViews());
- assertEquals(99, c1a.getDismissals());
-
- ChannelImpressions c2 = mAssistant.getImpressions(key2);
- assertEquals(7, c2.getStreak());
- assertEquals(77, c2.getViews());
- assertEquals(777, c2.getDismissals());
- }
-
- @Test
- public void testRoundTripXml() throws Exception {
- String key1 = mAssistant.getKey("pkg1", 1, "channel1");
- ChannelImpressions ci1 = new ChannelImpressions();
- String key2 = mAssistant.getKey("pkg1", 1, "channel2");
- ChannelImpressions ci2 = new ChannelImpressions();
- for (int i = 0; i < 3; i++) {
- ci2.incrementViews();
- ci2.incrementDismissals();
- }
- ChannelImpressions ci3 = new ChannelImpressions();
- String key3 = mAssistant.getKey("pkg3", 3, "channel2");
- for (int i = 0; i < 9; i++) {
- ci3.incrementViews();
- if (i % 3 == 0) {
- ci3.incrementDismissals();
- }
- }
-
- mAssistant.insertImpressions(key1, ci1);
- mAssistant.insertImpressions(key2, ci2);
- mAssistant.insertImpressions(key3, ci3);
-
- XmlSerializer serializer = new FastXmlSerializer();
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
- mAssistant.writeXml(serializer);
-
- Assistant assistant = new Assistant();
- // onCreate is not invoked, so settings won't be initialised, unless we do it here.
- assistant.mSettings = mAssistant.mSettings;
- assistant.readXml(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())));
-
- assertEquals(ci1, assistant.getImpressions(key1));
- assertEquals(ci2, assistant.getImpressions(key2));
- assertEquals(ci3, assistant.getImpressions(key3));
- }
-
- @Test
- public void testSettingsProviderUpdate() {
- // Set up channels
- String key = mAssistant.getKey("pkg1", 1, "channel1");
- ChannelImpressions ci = new ChannelImpressions();
- for (int i = 0; i < 3; i++) {
- ci.incrementViews();
- if (i % 2 == 0) {
- ci.incrementDismissals();
- }
- }
-
- mAssistant.insertImpressions(key, ci);
-
- // With default values, the blocking helper shouldn't be triggered.
- assertEquals(false, ci.shouldTriggerBlock());
-
- // Update settings values.
- mAssistant.mSettings.mDismissToViewRatioLimit = 0f;
- mAssistant.mSettings.mStreakLimit = 0;
-
- // Notify for the settings values we updated.
- mAssistant.mSettings.mOnUpdateRunnable.run();
-
- // With the new threshold, the blocking helper should be triggered.
- assertEquals(true, ci.shouldTriggerBlock());
- }
-
- @Test
- public void testTrimLiveNotifications() {
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null);
- mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
-
- mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
-
- assertTrue(mAssistant.mLiveNotifications.containsKey(sbn.getKey()));
-
- mAssistant.onNotificationRemoved(
- sbn, mock(RankingMap.class), new NotificationStats(), 0);
-
- assertFalse(mAssistant.mLiveNotifications.containsKey(sbn.getKey()));
- }
-
- @Test
- public void testAssistantNeverIncreasesImportanceWhenSuggestingSilent() throws Exception {
- StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C3, "min notif!", null);
- Adjustment adjust = mAssistant.createEnqueuedNotificationAdjustment(
- new NotificationEntry(mContext, mPackageManager, sbn, P1C3, mSmsHelper),
- new ArrayList<>(),
- new ArrayList<>());
- assertEquals(IMPORTANCE_MIN, adjust.getSignals().getInt(Adjustment.KEY_IMPORTANCE));
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java
deleted file mode 100644
index 3253802..0000000
--- a/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/**
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.ext.services.notification;
-
-import static android.ext.services.notification.ChannelImpressions.DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT;
-import static android.ext.services.notification.ChannelImpressions.DEFAULT_STREAK_LIMIT;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-
-public class ChannelImpressionsTest {
-
- @Test
- public void testNoResultNoBlock() {
- ChannelImpressions ci = new ChannelImpressions();
- assertFalse(ci.shouldTriggerBlock());
- }
-
- @Test
- public void testNoStreakNoBlock() {
- ChannelImpressions ci = new ChannelImpressions();
-
- for (int i = 0; i < DEFAULT_STREAK_LIMIT - 1; i++) {
- ci.incrementViews();
- ci.incrementDismissals();
- }
-
- assertFalse(ci.shouldTriggerBlock());
- }
-
- @Test
- public void testNoStreakNoBlock_breakStreak() {
- ChannelImpressions ci = new ChannelImpressions();
-
- for (int i = 0; i < DEFAULT_STREAK_LIMIT; i++) {
- ci.incrementViews();
- ci.incrementDismissals();
- if (i == DEFAULT_STREAK_LIMIT - 1) {
- ci.resetStreak();
- }
- }
-
- assertFalse(ci.shouldTriggerBlock());
- }
-
- @Test
- public void testStreakBlock() {
- ChannelImpressions ci = new ChannelImpressions();
-
- for (int i = 0; i <= DEFAULT_STREAK_LIMIT; i++) {
- ci.incrementViews();
- ci.incrementDismissals();
- }
-
- assertTrue(ci.shouldTriggerBlock());
- }
-
- @Test
- public void testRatio_NoBlockEvenWithStreak() {
- ChannelImpressions ci = new ChannelImpressions();
-
- for (int i = 0; i < DEFAULT_STREAK_LIMIT; i++) {
- ci.incrementViews();
- ci.incrementDismissals();
- ci.incrementViews();
- }
-
- assertFalse(ci.shouldTriggerBlock());
- }
-
- @Test
- public void testAppend() {
- ChannelImpressions ci = new ChannelImpressions();
- ci.incrementViews();
- ci.incrementDismissals();
-
- ChannelImpressions ci2 = new ChannelImpressions();
- ci2.incrementViews();
- ci2.incrementDismissals();
- ci2.incrementViews();
-
- ci.append(ci2);
- assertEquals(3, ci.getViews());
- assertEquals(2, ci.getDismissals());
- assertEquals(2, ci.getStreak());
-
- assertEquals(2, ci2.getViews());
- assertEquals(1, ci2.getDismissals());
- assertEquals(1, ci2.getStreak());
-
- // no crash
- ci.append(null);
- }
-
- @Test
- public void testUpdateThresholds_streakLimitsCorrectlyApplied() {
- int updatedStreakLimit = DEFAULT_STREAK_LIMIT + 3;
- ChannelImpressions ci = new ChannelImpressions();
- ci.updateThresholds(DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT, updatedStreakLimit);
-
- for (int i = 0; i <= updatedStreakLimit; i++) {
- ci.incrementViews();
- ci.incrementDismissals();
- }
-
- ChannelImpressions ci2 = new ChannelImpressions();
- ci2.updateThresholds(DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT, updatedStreakLimit);
-
- for (int i = 0; i < updatedStreakLimit; i++) {
- ci2.incrementViews();
- ci2.incrementDismissals();
- }
-
- assertTrue(ci.shouldTriggerBlock());
- assertFalse(ci2.shouldTriggerBlock());
- }
-
- @Test
- public void testUpdateThresholds_ratioLimitsCorrectlyApplied() {
- float updatedDismissRatio = .99f;
- ChannelImpressions ci = new ChannelImpressions();
- ci.updateThresholds(updatedDismissRatio, DEFAULT_STREAK_LIMIT);
-
- // N views, N-1 dismissals, which doesn't satisfy the ratio = 1 criteria.
- for (int i = 0; i <= DEFAULT_STREAK_LIMIT; i++) {
- ci.incrementViews();
- if (i != DEFAULT_STREAK_LIMIT) {
- ci.incrementDismissals();
- }
- }
-
- ChannelImpressions ci2 = new ChannelImpressions();
- ci2.updateThresholds(updatedDismissRatio, DEFAULT_STREAK_LIMIT);
-
- for (int i = 0; i <= DEFAULT_STREAK_LIMIT; i++) {
- ci2.incrementViews();
- ci2.incrementDismissals();
- }
-
- assertFalse(ci.shouldTriggerBlock());
- assertTrue(ci2.shouldTriggerBlock());
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/EntityTypeCounterTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/EntityTypeCounterTest.java
deleted file mode 100644
index ada61d0..0000000
--- a/packages/ExtServices/tests/src/android/ext/services/notification/EntityTypeCounterTest.java
+++ /dev/null
@@ -1,59 +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 android.ext.services.notification;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.view.textclassifier.TextClassifier;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class EntityTypeCounterTest {
- private EntityTypeCounter mCounter;
-
- @Before
- public void setup() {
- mCounter = new EntityTypeCounter();
- }
-
- @Test
- public void testIncrementAndGetCount() {
- mCounter.increment(TextClassifier.TYPE_URL);
- mCounter.increment(TextClassifier.TYPE_URL);
- mCounter.increment(TextClassifier.TYPE_URL);
-
- mCounter.increment(TextClassifier.TYPE_PHONE);
- mCounter.increment(TextClassifier.TYPE_PHONE);
-
- assertThat(mCounter.getCount(TextClassifier.TYPE_URL)).isEqualTo(3);
- assertThat(mCounter.getCount(TextClassifier.TYPE_PHONE)).isEqualTo(2);
- assertThat(mCounter.getCount(TextClassifier.TYPE_DATE_TIME)).isEqualTo(0);
- }
-
- @Test
- public void testIncrementAndGetCount_typeDateAndDateTime() {
- mCounter.increment(TextClassifier.TYPE_DATE_TIME);
- mCounter.increment(TextClassifier.TYPE_DATE);
-
- assertThat(mCounter.getCount(TextClassifier.TYPE_DATE_TIME)).isEqualTo(2);
- assertThat(mCounter.getCount(TextClassifier.TYPE_DATE)).isEqualTo(2);
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationCategorizerTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/NotificationCategorizerTest.java
deleted file mode 100644
index ea77d31..0000000
--- a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationCategorizerTest.java
+++ /dev/null
@@ -1,202 +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 android.ext.services.notification;
-
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.app.NotificationManager.IMPORTANCE_LOW;
-import static android.app.NotificationManager.IMPORTANCE_MIN;
-import static android.os.Process.FIRST_APPLICATION_UID;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.when;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.os.Process;
-import android.service.notification.StatusBarNotification;
-import android.testing.TestableContext;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(AndroidJUnit4.class)
-public class NotificationCategorizerTest {
- @Mock
- private NotificationEntry mEntry;
- @Mock
- private StatusBarNotification mSbn;
-
- @Rule
- public final TestableContext mContext =
- new TestableContext(InstrumentationRegistry.getContext(), null);
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- when(mEntry.getSbn()).thenReturn(mSbn);
- when(mSbn.getUid()).thenReturn(Process.myUid());
- when(mSbn.getPackageName()).thenReturn(mContext.getPackageName());
- }
-
- @Test
- public void testPeopleCategory() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_DEFAULT));
- when(mEntry.involvesPeople()).thenReturn(true);
-
- assertEquals(NotificationCategorizer.CATEGORY_PEOPLE, nc.getCategory(mEntry));
- assertFalse(nc.shouldSilence(NotificationCategorizer.CATEGORY_PEOPLE));
- }
-
- @Test
- public void testMin() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_MIN));
- when(mEntry.involvesPeople()).thenReturn(true);
-
- assertEquals(NotificationCategorizer.CATEGORY_MIN, nc.getCategory(mEntry));
- assertTrue(nc.shouldSilence(NotificationCategorizer.CATEGORY_MIN));
- }
-
- @Test
- public void testHigh() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_HIGH));
-
- assertEquals(NotificationCategorizer.CATEGORY_HIGH, nc.getCategory(mEntry));
- assertFalse(nc.shouldSilence(NotificationCategorizer.CATEGORY_HIGH));
- }
-
- @Test
- public void testOngoingCategory() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_DEFAULT));
- when(mEntry.isOngoing()).thenReturn(true);
-
- assertEquals(NotificationCategorizer.CATEGORY_ONGOING, nc.getCategory(mEntry));
- assertTrue(nc.shouldSilence(NotificationCategorizer.CATEGORY_ONGOING));
-
- when(mEntry.isOngoing()).thenReturn(false);
- assertEquals(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE, nc.getCategory(mEntry));
- assertTrue(nc.shouldSilence(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE));
- }
-
- @Test
- public void testAlarmCategory() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_DEFAULT));
- when(mEntry.isCategory(Notification.CATEGORY_ALARM)).thenReturn(true);
-
- assertEquals(NotificationCategorizer.CATEGORY_ALARM, nc.getCategory(mEntry));
- assertFalse(nc.shouldSilence(NotificationCategorizer.CATEGORY_ALARM));
-
- when(mEntry.isCategory(Notification.CATEGORY_ALARM)).thenReturn(false);
- assertEquals(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE, nc.getCategory(mEntry));
- assertTrue(nc.shouldSilence(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE));
- }
-
- @Test
- public void testCallCategory() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_DEFAULT));
- when(mEntry.isCategory(Notification.CATEGORY_CALL)).thenReturn(true);
-
- assertEquals(NotificationCategorizer.CATEGORY_CALL, nc.getCategory(mEntry));
- assertFalse(nc.shouldSilence(NotificationCategorizer.CATEGORY_CALL));
-
- when(mEntry.isCategory(Notification.CATEGORY_CALL)).thenReturn(false);
- assertEquals(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE, nc.getCategory(mEntry));
- assertTrue(nc.shouldSilence(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE));
- }
-
- @Test
- public void testReminderCategory() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_DEFAULT));
- when(mEntry.isCategory(Notification.CATEGORY_REMINDER)).thenReturn(true);
-
- assertEquals(NotificationCategorizer.CATEGORY_REMINDER, nc.getCategory(mEntry));
- assertFalse(nc.shouldSilence(NotificationCategorizer.CATEGORY_REMINDER));
-
- when(mEntry.isCategory(Notification.CATEGORY_REMINDER)).thenReturn(false);
- assertEquals(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE, nc.getCategory(mEntry));
- assertTrue(nc.shouldSilence(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE));
- }
-
- @Test
- public void testEventCategory() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_DEFAULT));
- when(mEntry.isCategory(Notification.CATEGORY_EVENT)).thenReturn(true);
-
- assertEquals(NotificationCategorizer.CATEGORY_EVENT, nc.getCategory(mEntry));
- assertFalse(nc.shouldSilence(NotificationCategorizer.CATEGORY_EVENT));
-
- when(mEntry.isCategory(Notification.CATEGORY_EVENT)).thenReturn(false);
- assertEquals(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE, nc.getCategory(mEntry));
- }
-
- @Test
- public void testSystemCategory() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_DEFAULT));
- when(mEntry.getImportance()).thenReturn(IMPORTANCE_DEFAULT);
- when(mSbn.getUid()).thenReturn(FIRST_APPLICATION_UID - 1);
-
- assertEquals(NotificationCategorizer.CATEGORY_SYSTEM, nc.getCategory(mEntry));
- assertFalse(nc.shouldSilence(NotificationCategorizer.CATEGORY_SYSTEM));
-
- when(mSbn.getUid()).thenReturn(FIRST_APPLICATION_UID);
- assertEquals(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE, nc.getCategory(mEntry));
- }
-
- @Test
- public void testSystemLowCategory() {
- NotificationCategorizer nc = new NotificationCategorizer();
-
- when(mEntry.getChannel()).thenReturn(new NotificationChannel("", "", IMPORTANCE_LOW));
- when(mEntry.getImportance()).thenReturn(IMPORTANCE_LOW);
- when(mSbn.getUid()).thenReturn(FIRST_APPLICATION_UID - 1);
-
- assertEquals(NotificationCategorizer.CATEGORY_SYSTEM_LOW, nc.getCategory(mEntry));
- assertTrue(nc.shouldSilence(NotificationCategorizer.CATEGORY_SYSTEM_LOW));
-
- when(mSbn.getUid()).thenReturn(FIRST_APPLICATION_UID);
- assertEquals(NotificationCategorizer.CATEGORY_EVERYTHING_ELSE, nc.getCategory(mEntry));
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java
deleted file mode 100644
index c026079..0000000
--- a/packages/ExtServices/tests/src/android/ext/services/notification/NotificationEntryTest.java
+++ /dev/null
@@ -1,266 +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 android.ext.services.notification;
-
-import static android.app.Notification.FLAG_CAN_COLORIZE;
-import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
-import static android.app.NotificationManager.IMPORTANCE_HIGH;
-import static android.media.AudioAttributes.USAGE_ALARM;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.when;
-
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.Person;
-import android.content.ComponentName;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.graphics.Bitmap;
-import android.graphics.drawable.Icon;
-import android.media.AudioAttributes;
-import android.os.Build;
-import android.os.Process;
-import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
-import android.testing.TestableContext;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-@RunWith(AndroidJUnit4.class)
-public class NotificationEntryTest {
- private String mPkg = "pkg";
- private int mUid = 2018;
- @Mock
- private IPackageManager mPackageManager;
- @Mock
- private ApplicationInfo mAppInfo;
- @Mock
- private SmsHelper mSmsHelper;
-
- private static final String DEFAULT_SMS_PACKAGE_NAME = "foo";
-
- @Rule
- public final TestableContext mContext =
- new TestableContext(InstrumentationRegistry.getContext(), null);
-
- private StatusBarNotification generateSbn(String channelId) {
- Notification n = new Notification.Builder(mContext, channelId)
- .setContentTitle("foo")
- .build();
-
- return new StatusBarNotification(mPkg, mPkg, 0, "tag", mUid, mUid, n,
- UserHandle.SYSTEM, null, 0);
- }
-
- private StatusBarNotification generateSbn(String channelId, String packageName) {
- Notification n = new Notification.Builder(mContext, channelId)
- .setContentTitle("foo")
- .build();
-
- return new StatusBarNotification(packageName, packageName, 0, "tag", mUid, mUid, n,
- UserHandle.SYSTEM, null, 0);
- }
-
- private StatusBarNotification generateSbn(Notification n) {
- return new StatusBarNotification(mPkg, mPkg, 0, "tag", mUid, mUid, n,
- UserHandle.SYSTEM, null, 0);
- }
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mPkg = mContext.getPackageName();
- mUid = Process.myUid();
- when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt()))
- .thenReturn(mAppInfo);
- mAppInfo.targetSdkVersion = Build.VERSION_CODES.P;
- when(mSmsHelper.getDefaultSmsApplication())
- .thenReturn(new ComponentName(DEFAULT_SMS_PACKAGE_NAME, "bar"));
- }
-
- @Test
- public void testHasPerson() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- StatusBarNotification sbn = generateSbn(channel.getId());
- ArrayList<Person> people = new ArrayList<>();
- people.add(new Person.Builder().setKey("mailto:testing@android.com").build());
- sbn.getNotification().extras.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, people);
-
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, sbn, channel, mSmsHelper);
- assertTrue(entry.involvesPeople());
- }
-
- @Test
- public void testNotPerson() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- StatusBarNotification sbn = generateSbn(channel.getId());
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, sbn, channel, mSmsHelper);
- assertFalse(entry.involvesPeople());
- }
-
- @Test
- public void testHasPerson_matchesDefaultSmsApp() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- StatusBarNotification sbn = generateSbn(channel.getId(), DEFAULT_SMS_PACKAGE_NAME);
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, sbn, channel, mSmsHelper);
- assertTrue(entry.involvesPeople());
- }
-
- @Test
- public void testHasPerson_doesntMatchDefaultSmsApp() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- StatusBarNotification sbn = generateSbn(channel.getId(), "abc");
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, sbn, channel, mSmsHelper);
- assertFalse(entry.involvesPeople());
- }
-
- @Test
- public void testIsInboxStyle() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
-
- Notification n = new Notification.Builder(mContext, channel.getId())
- .setStyle(new Notification.InboxStyle())
- .build();
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
- assertTrue(entry.hasStyle(Notification.InboxStyle.class));
- }
-
- @Test
- public void testIsMessagingStyle() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
-
- Notification n = new Notification.Builder(mContext, channel.getId())
- .setStyle(new Notification.MessagingStyle(""))
- .build();
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
- assertTrue(entry.hasStyle(Notification.MessagingStyle.class));
- }
-
- @Test
- public void testIsNotPersonStyle() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
-
- Notification n = new Notification.Builder(mContext, channel.getId())
- .setStyle(new Notification.BigPictureStyle())
- .build();
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
- assertFalse(entry.hasStyle(Notification.InboxStyle.class));
- assertFalse(entry.hasStyle(Notification.MessagingStyle.class));
- }
-
- @Test
- public void testIsAudioAttributes() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- channel.setSound(null, new AudioAttributes.Builder().setUsage(USAGE_ALARM).build());
-
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper);
-
- assertTrue(entry.isAudioAttributesUsage(USAGE_ALARM));
- }
-
- @Test
- public void testIsNotAudioAttributes() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, generateSbn(channel.getId()), channel, mSmsHelper);
-
- assertFalse(entry.isAudioAttributesUsage(USAGE_ALARM));
- }
-
- @Test
- public void testIsCategory() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
-
- Notification n = new Notification.Builder(mContext, channel.getId())
- .setCategory(Notification.CATEGORY_EMAIL)
- .build();
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
-
- assertTrue(entry.isCategory(Notification.CATEGORY_EMAIL));
- assertFalse(entry.isCategory(Notification.CATEGORY_MESSAGE));
- }
-
- @Test
- public void testIsOngoing() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
-
- Notification n = new Notification.Builder(mContext, channel.getId())
- .setFlag(FLAG_FOREGROUND_SERVICE, true)
- .build();
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
-
- assertTrue(entry.isOngoing());
- }
-
- @Test
- public void testIsNotOngoing() {
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
-
- Notification n = new Notification.Builder(mContext, channel.getId())
- .setFlag(FLAG_CAN_COLORIZE, true)
- .build();
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
-
- assertFalse(entry.isOngoing());
- }
-
- @Test
- public void testShrinkNotification() {
- Notification n = new Notification.Builder(mContext, "")
- .setLargeIcon(Icon.createWithResource(
- mContext, android.R.drawable.alert_dark_frame))
- .setSmallIcon(android.R.drawable.sym_def_app_icon)
- .build();
- n.largeIcon = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
- NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
-
- NotificationEntry entry = new NotificationEntry(
- mContext, mPackageManager, generateSbn(n), channel, mSmsHelper);
-
- assertNull(entry.getNotification().getSmallIcon());
- assertNull(entry.getNotification().getLargeIcon());
- assertNull(entry.getNotification().largeIcon);
- assertNull(entry.getNotification().extras.getParcelable(Notification.EXTRA_LARGE_ICON));
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
deleted file mode 100644
index 69abe87..0000000
--- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionsHelperTest.java
+++ /dev/null
@@ -1,564 +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 android.ext.services.notification;
-
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.annotation.NonNull;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Person;
-import android.app.RemoteInput;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.IPackageManager;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.os.Process;
-import android.service.notification.NotificationAssistantService;
-import android.service.notification.StatusBarNotification;
-import android.view.textclassifier.ConversationAction;
-import android.view.textclassifier.ConversationActions;
-import android.view.textclassifier.TextClassificationManager;
-import android.view.textclassifier.TextClassifier;
-import android.view.textclassifier.TextClassifierEvent;
-
-import com.google.common.truth.FailureStrategy;
-import com.google.common.truth.Subject;
-import com.google.common.truth.SubjectFactory;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.time.Instant;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-import javax.annotation.Nullable;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-@RunWith(AndroidJUnit4.class)
-public class SmartActionsHelperTest {
- private static final String RESULT_ID = "id";
- private static final float SCORE = 0.7f;
- private static final CharSequence SMART_REPLY = "Home";
- private static final ConversationAction REPLY_ACTION =
- new ConversationAction.Builder(ConversationAction.TYPE_TEXT_REPLY)
- .setTextReply(SMART_REPLY)
- .setConfidenceScore(SCORE)
- .build();
- private static final String MESSAGE = "Where are you?";
-
- @Mock
- IPackageManager mIPackageManager;
- @Mock
- private TextClassifier mTextClassifier;
- private StatusBarNotification mStatusBarNotification;
- @Mock
- private SmsHelper mSmsHelper;
-
- private SmartActionsHelper mSmartActionsHelper;
- private Context mContext;
- private Notification.Builder mNotificationBuilder;
- private AssistantSettings mSettings;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mContext = InstrumentationRegistry.getTargetContext();
-
- mContext.getSystemService(TextClassificationManager.class)
- .setTextClassifier(mTextClassifier);
- when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
- .thenReturn(new ConversationActions(Arrays.asList(REPLY_ACTION), RESULT_ID));
-
- mNotificationBuilder = new Notification.Builder(mContext, "channel");
- mSettings = AssistantSettings.createForTesting(
- null, null, Process.myUserHandle().getIdentifier(), null);
- mSettings.mGenerateActions = true;
- mSettings.mGenerateReplies = true;
- mSmartActionsHelper = new SmartActionsHelper(mContext, mSettings);
- }
-
- private void setStatusBarNotification(Notification n) {
- mStatusBarNotification = new StatusBarNotification("random.app", "random.app", 0,
- "tag", Process.myUid(), Process.myPid(), n, Process.myUserHandle(), null, 0);
- }
-
- @Test
- public void testSuggest_notMessageNotification() {
- Notification notification = mNotificationBuilder.setContentText(MESSAGE).build();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
-
- verify(mTextClassifier, never())
- .suggestConversationActions(any(ConversationActions.Request.class));
- }
-
- @Test
- public void testSuggest_noInlineReply() {
- Notification notification =
- mNotificationBuilder
- .setContentText(MESSAGE)
- .setCategory(Notification.CATEGORY_MESSAGE)
- .build();
- setStatusBarNotification(notification);
-
- ConversationActions.Request request = runSuggestAndCaptureRequest();
-
- // actions are enabled, but replies are not.
- assertThat(
- request.getTypeConfig().resolveEntityListModifications(
- Arrays.asList(ConversationAction.TYPE_TEXT_REPLY,
- ConversationAction.TYPE_OPEN_URL)))
- .containsExactly(ConversationAction.TYPE_OPEN_URL);
- }
-
- @Test
- public void testSuggest_settingsOff() {
- mSettings.mGenerateActions = false;
- mSettings.mGenerateReplies = false;
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
-
- verify(mTextClassifier, never())
- .suggestConversationActions(any(ConversationActions.Request.class));
- }
-
- @Test
- public void testSuggest_settings_repliesOnActionsOff() {
- mSettings.mGenerateReplies = true;
- mSettings.mGenerateActions = false;
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- ConversationActions.Request request = runSuggestAndCaptureRequest();
-
- // replies are enabled, but actions are not.
- assertThat(
- request.getTypeConfig().resolveEntityListModifications(
- Arrays.asList(ConversationAction.TYPE_TEXT_REPLY,
- ConversationAction.TYPE_OPEN_URL)))
- .containsExactly(ConversationAction.TYPE_TEXT_REPLY);
- }
-
- @Test
- public void testSuggest_settings_repliesOffActionsOn() {
- mSettings.mGenerateReplies = false;
- mSettings.mGenerateActions = true;
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- ConversationActions.Request request = runSuggestAndCaptureRequest();
-
- // actions are enabled, but replies are not.
- assertThat(
- request.getTypeConfig().resolveEntityListModifications(
- Arrays.asList(ConversationAction.TYPE_TEXT_REPLY,
- ConversationAction.TYPE_OPEN_URL)))
- .containsExactly(ConversationAction.TYPE_OPEN_URL);
- }
-
-
- @Test
- public void testSuggest_nonMessageStyleMessageNotification() {
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- List<ConversationActions.Message> messages =
- runSuggestAndCaptureRequest().getConversation();
-
- assertThat(messages).hasSize(1);
- MessageSubject.assertThat(messages.get(0)).hasText(MESSAGE);
- ArgumentCaptor<TextClassifierEvent> argumentCaptor =
- ArgumentCaptor.forClass(TextClassifierEvent.class);
- verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
- TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
- assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_GENERATED);
- assertThat(textClassifierEvent.getEntityTypes()).asList()
- .containsExactly(ConversationAction.TYPE_TEXT_REPLY);
- }
-
- @Test
- public void testSuggest_messageStyle() {
- Person me = new Person.Builder().setName("Me").build();
- Person userA = new Person.Builder().setName("A").build();
- Person userB = new Person.Builder().setName("B").build();
- Notification.MessagingStyle style =
- new Notification.MessagingStyle(me)
- .addMessage("firstMessage", 1000, (Person) null)
- .addMessage("secondMessage", 2000, me)
- .addMessage("thirdMessage", 3000, userA)
- .addMessage("fourthMessage", 4000, userB);
- Notification notification =
- mNotificationBuilder
- .setContentText("You have three new messages")
- .setStyle(style)
- .setActions(createReplyAction())
- .build();
- setStatusBarNotification(notification);
-
- List<ConversationActions.Message> messages =
- runSuggestAndCaptureRequest().getConversation();
- assertThat(messages).hasSize(4);
-
- ConversationActions.Message firstMessage = messages.get(0);
- MessageSubject.assertThat(firstMessage).hasText("firstMessage");
- MessageSubject.assertThat(firstMessage)
- .hasPerson(ConversationActions.Message.PERSON_USER_SELF);
- MessageSubject.assertThat(firstMessage)
- .hasReferenceTime(createZonedDateTimeFromMsUtc(1000));
-
- ConversationActions.Message secondMessage = messages.get(1);
- MessageSubject.assertThat(secondMessage).hasText("secondMessage");
- MessageSubject.assertThat(secondMessage)
- .hasPerson(ConversationActions.Message.PERSON_USER_SELF);
- MessageSubject.assertThat(secondMessage)
- .hasReferenceTime(createZonedDateTimeFromMsUtc(2000));
-
- ConversationActions.Message thirdMessage = messages.get(2);
- MessageSubject.assertThat(thirdMessage).hasText("thirdMessage");
- MessageSubject.assertThat(thirdMessage).hasPerson(userA);
- MessageSubject.assertThat(thirdMessage)
- .hasReferenceTime(createZonedDateTimeFromMsUtc(3000));
-
- ConversationActions.Message fourthMessage = messages.get(3);
- MessageSubject.assertThat(fourthMessage).hasText("fourthMessage");
- MessageSubject.assertThat(fourthMessage).hasPerson(userB);
- MessageSubject.assertThat(fourthMessage)
- .hasReferenceTime(createZonedDateTimeFromMsUtc(4000));
-
- ArgumentCaptor<TextClassifierEvent> argumentCaptor =
- ArgumentCaptor.forClass(TextClassifierEvent.class);
- verify(mTextClassifier).onTextClassifierEvent(argumentCaptor.capture());
- TextClassifierEvent textClassifierEvent = argumentCaptor.getValue();
- assertTextClassifierEvent(textClassifierEvent, TextClassifierEvent.TYPE_ACTIONS_GENERATED);
- assertThat(textClassifierEvent.getEntityTypes()).asList()
- .containsExactly(ConversationAction.TYPE_TEXT_REPLY);
- }
-
- @Test
- public void testSuggest_lastMessageLocalUser() {
- Person me = new Person.Builder().setName("Me").build();
- Person userA = new Person.Builder().setName("A").build();
- Notification.MessagingStyle style =
- new Notification.MessagingStyle(me)
- .addMessage("firstMessage", 1000, userA)
- .addMessage("secondMessage", 2000, me);
- Notification notification =
- mNotificationBuilder
- .setContentText("You have two new messages")
- .setStyle(style)
- .setActions(createReplyAction())
- .build();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
-
- verify(mTextClassifier, never())
- .suggestConversationActions(any(ConversationActions.Request.class));
- }
-
- @Test
- public void testSuggest_messageStyle_noPerson() {
- Person me = new Person.Builder().setName("Me").build();
- Notification.MessagingStyle style =
- new Notification.MessagingStyle(me).addMessage("message", 1000, (Person) null);
- Notification notification =
- mNotificationBuilder
- .setContentText("You have one new message")
- .setStyle(style)
- .setActions(createReplyAction())
- .build();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
-
- verify(mTextClassifier, never())
- .suggestConversationActions(any(ConversationActions.Request.class));
- }
-
- @Test
- public void testOnSuggestedReplySent() {
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
- mSmartActionsHelper.onSuggestedReplySent(mStatusBarNotification.getKey(), SMART_REPLY,
- NotificationAssistantService.SOURCE_FROM_ASSISTANT);
-
- ArgumentCaptor<TextClassifierEvent> argumentCaptor =
- ArgumentCaptor.forClass(TextClassifierEvent.class);
- verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture());
- List<TextClassifierEvent> events = argumentCaptor.getAllValues();
- assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED);
- assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_SMART_ACTION);
- float[] scores = events.get(1).getScores();
- assertThat(scores).hasLength(1);
- assertThat(scores[0]).isEqualTo(SCORE);
- }
-
- @Test
- public void testOnSuggestedReplySent_anotherNotification() {
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
- mSmartActionsHelper.onSuggestedReplySent(
- "something_else", MESSAGE, NotificationAssistantService.SOURCE_FROM_ASSISTANT);
-
- verify(mTextClassifier, never()).onTextClassifierEvent(
- argThat(new TextClassifierEventMatcher(TextClassifierEvent.TYPE_SMART_ACTION)));
- }
-
- @Test
- public void testOnSuggestedReplySent_missingResultId() {
- when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
- .thenReturn(new ConversationActions(Collections.singletonList(REPLY_ACTION), null));
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
- mSmartActionsHelper.onSuggestedReplySent(mStatusBarNotification.getKey(), SMART_REPLY,
- NotificationAssistantService.SOURCE_FROM_ASSISTANT);
-
- verify(mTextClassifier, never()).onTextClassifierEvent(any(TextClassifierEvent.class));
- }
-
- @Test
- public void testOnNotificationDirectReply() {
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
- mSmartActionsHelper.onNotificationDirectReplied(mStatusBarNotification.getKey());
-
- ArgumentCaptor<TextClassifierEvent> argumentCaptor =
- ArgumentCaptor.forClass(TextClassifierEvent.class);
- verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture());
- List<TextClassifierEvent> events = argumentCaptor.getAllValues();
- assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED);
- assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_MANUAL_REPLY);
- }
-
- @Test
- public void testOnNotificationExpansionChanged() {
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
- mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), true);
-
- ArgumentCaptor<TextClassifierEvent> argumentCaptor =
- ArgumentCaptor.forClass(TextClassifierEvent.class);
- verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture());
- List<TextClassifierEvent> events = argumentCaptor.getAllValues();
- assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED);
- assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_ACTIONS_SHOWN);
- }
-
- @Test
- public void testOnNotificationsSeen_notExpanded() {
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
- mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), false);
-
- verify(mTextClassifier, never()).onTextClassifierEvent(
- argThat(new TextClassifierEventMatcher(TextClassifierEvent.TYPE_ACTIONS_SHOWN)));
- }
-
- @Test
- public void testOnNotifications_expanded() {
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
-
- mSmartActionsHelper.suggest(createNotificationEntry());
- mSmartActionsHelper.onNotificationExpansionChanged(createNotificationEntry(), true);
-
- ArgumentCaptor<TextClassifierEvent> argumentCaptor =
- ArgumentCaptor.forClass(TextClassifierEvent.class);
- verify(mTextClassifier, times(2)).onTextClassifierEvent(argumentCaptor.capture());
- List<TextClassifierEvent> events = argumentCaptor.getAllValues();
- assertTextClassifierEvent(events.get(0), TextClassifierEvent.TYPE_ACTIONS_GENERATED);
- assertTextClassifierEvent(events.get(1), TextClassifierEvent.TYPE_ACTIONS_SHOWN);
- }
-
- @Test
- public void testCopyAction() {
- Bundle extras = new Bundle();
- Bundle entitiesExtras = new Bundle();
- entitiesExtras.putString(SmartActionsHelper.KEY_TEXT, "12345");
- extras.putParcelable(SmartActionsHelper.ENTITIES_EXTRAS, entitiesExtras);
- ConversationAction conversationAction =
- new ConversationAction.Builder(ConversationAction.TYPE_COPY)
- .setExtras(extras)
- .build();
- when(mTextClassifier.suggestConversationActions(any(ConversationActions.Request.class)))
- .thenReturn(
- new ConversationActions(
- Collections.singletonList(conversationAction), null));
-
- Notification notification = createMessageNotification();
- setStatusBarNotification(notification);
- SmartActionsHelper.SmartSuggestions suggestions =
- mSmartActionsHelper.suggest(createNotificationEntry());
-
- assertThat(suggestions.actions).hasSize(1);
- Notification.Action action = suggestions.actions.get(0);
- assertThat(action.title).isEqualTo("12345");
- }
-
- private ZonedDateTime createZonedDateTimeFromMsUtc(long msUtc) {
- return ZonedDateTime.ofInstant(Instant.ofEpochMilli(msUtc), ZoneOffset.systemDefault());
- }
-
- private ConversationActions.Request runSuggestAndCaptureRequest() {
- mSmartActionsHelper.suggest(createNotificationEntry());
-
- ArgumentCaptor<ConversationActions.Request> argumentCaptor =
- ArgumentCaptor.forClass(ConversationActions.Request.class);
- verify(mTextClassifier).suggestConversationActions(argumentCaptor.capture());
- return argumentCaptor.getValue();
- }
-
- private Notification.Action createReplyAction() {
- PendingIntent pendingIntent =
- PendingIntent.getActivity(mContext, 0, new Intent(mContext, this.getClass()), 0);
- RemoteInput remoteInput = new RemoteInput.Builder("result")
- .setAllowFreeFormInput(true)
- .build();
- return new Notification.Action.Builder(
- Icon.createWithResource(mContext.getResources(),
- android.R.drawable.stat_sys_warning),
- "Reply", pendingIntent)
- .addRemoteInput(remoteInput)
- .build();
- }
-
- private NotificationEntry createNotificationEntry() {
- NotificationChannel channel =
- new NotificationChannel("id", "name", NotificationManager.IMPORTANCE_DEFAULT);
- return new NotificationEntry(
- mContext, mIPackageManager, mStatusBarNotification, channel, mSmsHelper);
- }
-
- private Notification createMessageNotification() {
- return mNotificationBuilder
- .setContentText(MESSAGE)
- .setCategory(Notification.CATEGORY_MESSAGE)
- .setActions(createReplyAction())
- .build();
- }
-
- private void assertTextClassifierEvent(
- TextClassifierEvent textClassifierEvent, int expectedEventType) {
- assertThat(textClassifierEvent.getEventCategory())
- .isEqualTo(TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS);
- assertThat(textClassifierEvent.getEventContext().getPackageName())
- .isEqualTo(InstrumentationRegistry.getTargetContext().getPackageName());
- assertThat(textClassifierEvent.getEventContext().getWidgetType())
- .isEqualTo(TextClassifier.WIDGET_TYPE_NOTIFICATION);
- assertThat(textClassifierEvent.getEventType()).isEqualTo(expectedEventType);
- }
-
- private static final class MessageSubject
- extends Subject<MessageSubject, ConversationActions.Message> {
-
- private static final SubjectFactory<MessageSubject, ConversationActions.Message> FACTORY =
- new SubjectFactory<MessageSubject, ConversationActions.Message>() {
- @Override
- public MessageSubject getSubject(
- @NonNull FailureStrategy failureStrategy,
- @NonNull ConversationActions.Message subject) {
- return new MessageSubject(failureStrategy, subject);
- }
- };
-
- private MessageSubject(
- FailureStrategy failureStrategy, @Nullable ConversationActions.Message subject) {
- super(failureStrategy, subject);
- }
-
- private void hasText(String text) {
- if (!Objects.equals(text, getSubject().getText().toString())) {
- failWithBadResults("has text", text, "has", getSubject().getText());
- }
- }
-
- private void hasPerson(Person person) {
- if (!Objects.equals(person, getSubject().getAuthor())) {
- failWithBadResults("has author", person, "has", getSubject().getAuthor());
- }
- }
-
- private void hasReferenceTime(ZonedDateTime referenceTime) {
- if (!Objects.equals(referenceTime, getSubject().getReferenceTime())) {
- failWithBadResults(
- "has reference time",
- referenceTime,
- "has",
- getSubject().getReferenceTime());
- }
- }
-
- private static MessageSubject assertThat(ConversationActions.Message message) {
- return assertAbout(FACTORY).that(message);
- }
- }
-
- private final class TextClassifierEventMatcher implements ArgumentMatcher<TextClassifierEvent> {
-
- private int mType;
-
- private TextClassifierEventMatcher(int type) {
- mType = type;
- }
-
- @Override
- public boolean matches(TextClassifierEvent textClassifierEvent) {
- if (textClassifierEvent == null) {
- return false;
- }
- return mType == textClassifierEvent.getEventType();
- }
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/sms/FinancialSmsServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/sms/FinancialSmsServiceImplTest.java
deleted file mode 100644
index 12575a6..0000000
--- a/packages/ExtServices/tests/src/android/ext/services/sms/FinancialSmsServiceImplTest.java
+++ /dev/null
@@ -1,35 +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 android.ext.services.sms;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Bundle;
-
-import org.junit.Test;
-
-/**
- * Contains the base tests for FinancialSmsServiceImpl.
- */
-public class FinancialSmsServiceImplTest {
-
- private final FinancialSmsServiceImpl mService = new FinancialSmsServiceImpl();
-
- @Test
- public void testOnGetSmsMessages_nullWithNoParamData() {
- assertThat(mService.onGetSmsMessages(new Bundle())).isNull();
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/storage/CacheQuotaServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/storage/CacheQuotaServiceImplTest.java
deleted file mode 100644
index df4738f..0000000
--- a/packages/ExtServices/tests/src/android/ext/services/storage/CacheQuotaServiceImplTest.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.ext.services.storage;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.when;
-
-import android.app.usage.CacheQuotaHint;
-import android.app.usage.UsageStats;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.os.storage.StorageManager;
-import android.os.storage.VolumeInfo;
-import android.test.ServiceTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Answers;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-public class CacheQuotaServiceImplTest extends ServiceTestCase<CacheQuotaServiceImpl> {
- private static final String sTestVolUuid = "uuid";
- private static final String sSecondTestVolUuid = "otherUuid";
-
- @Mock private Context mContext;
- @Mock private File mFile;
- @Mock private VolumeInfo mVolume;
- @Mock(answer = Answers.RETURNS_DEEP_STUBS) private StorageManager mStorageManager;
-
- public CacheQuotaServiceImplTest() {
- super(CacheQuotaServiceImpl.class);
- }
-
- @Before
- public void setUp() throws Exception {
- super.setUp();
- MockitoAnnotations.initMocks(this);
- mContext = Mockito.spy(new ContextWrapper(getSystemContext()));
- setContext(mContext);
- when(mContext.getSystemService(Context.STORAGE_SERVICE)).thenReturn(mStorageManager);
-
- when(mFile.getUsableSpace()).thenReturn(10000L);
- when(mVolume.getPath()).thenReturn(mFile);
- when(mStorageManager.findVolumeByUuid(sTestVolUuid)).thenReturn(mVolume);
- when(mStorageManager.findVolumeByUuid(sSecondTestVolUuid)).thenReturn(mVolume);
-
- Intent intent = new Intent(getContext(), CacheQuotaServiceImpl.class);
- startService(intent);
- }
-
- @Test
- public void testNoApps() {
- CacheQuotaServiceImpl service = getService();
- assertEquals(service.onComputeCacheQuotaHints(new ArrayList()).size(), 0);
- }
-
- @Test
- public void testOneApp() throws Exception {
- ArrayList<CacheQuotaHint> requests = new ArrayList<>();
- CacheQuotaHint request = makeNewRequest("com.test", sTestVolUuid, 1001, 100L);
- requests.add(request);
-
- List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests);
-
- assertThat(output).hasSize(1);
- assertThat(output.get(0).getQuota()).isEqualTo(1500L);
- }
-
- @Test
- public void testTwoAppsOneVolume() throws Exception {
- ArrayList<CacheQuotaHint> requests = new ArrayList<>();
- requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L));
- requests.add(makeNewRequest("com.test2", sTestVolUuid, 1002, 99L));
-
- List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests);
-
- // Note that the sizes are just the cache area split up.
- assertThat(output).hasSize(2);
- assertThat(output.get(0).getQuota()).isEqualTo(883);
- assertThat(output.get(1).getQuota()).isEqualTo(1500 - 883);
- }
-
- @Test
- public void testTwoAppsTwoVolumes() throws Exception {
- ArrayList<CacheQuotaHint> requests = new ArrayList<>();
- requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L));
- requests.add(makeNewRequest("com.test2", sSecondTestVolUuid, 1002, 99L));
-
- List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests);
-
- assertThat(output).hasSize(2);
- assertThat(output.get(0).getQuota()).isEqualTo(1500);
- assertThat(output.get(1).getQuota()).isEqualTo(1500);
- }
-
- @Test
- public void testMultipleAppsPerUidIsCollated() throws Exception {
- ArrayList<CacheQuotaHint> requests = new ArrayList<>();
- requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L));
- requests.add(makeNewRequest("com.test2", sTestVolUuid, 1001, 99L));
-
- List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests);
-
- assertThat(output).hasSize(1);
- assertThat(output.get(0).getQuota()).isEqualTo(1500);
- }
-
- @Test
- public void testTwoAppsTwoVolumesTwoUuidsShouldBESeparate() throws Exception {
- ArrayList<CacheQuotaHint> requests = new ArrayList<>();
- requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L));
- requests.add(makeNewRequest("com.test2", sSecondTestVolUuid, 1001, 99L));
-
- List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests);
-
- assertThat(output).hasSize(2);
- assertThat(output.get(0).getQuota()).isEqualTo(1500);
- assertThat(output.get(1).getQuota()).isEqualTo(1500);
- }
-
- private CacheQuotaHint makeNewRequest(String packageName, String uuid, int uid, long foregroundTime) {
- UsageStats stats = new UsageStats();
- stats.mPackageName = packageName;
- stats.mTotalTimeInForeground = foregroundTime;
- return new CacheQuotaHint.Builder()
- .setVolumeUuid(uuid).setUid(uid).setUsageStats(stats).setQuota(-1).build();
- }
-}
diff --git a/packages/ExtServices/tests/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImplTest.java
deleted file mode 100644
index a9cb63e..0000000
--- a/packages/ExtServices/tests/src/android/ext/services/watchdog/ExplicitHealthCheckServiceImplTest.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.ext.services.watchdog;
-
-import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_REQUESTED_PACKAGES;
-import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_SUPPORTED_PACKAGES;
-import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeFalse;
-
-import android.Manifest;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.RemoteCallback;
-import android.service.watchdog.ExplicitHealthCheckService;
-import android.service.watchdog.IExplicitHealthCheckService;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.rule.ServiceTestRule;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-
-/**
- * Contains the base tests that does not rely on the specific algorithm implementation.
- */
-public class ExplicitHealthCheckServiceImplTest {
- private static final String NETWORK_STACK_CONNECTOR_CLASS =
- "android.net.INetworkStackConnector";
-
- private final Context mContext = InstrumentationRegistry.getContext();
- private IExplicitHealthCheckService mService;
- private String mNetworkStackPackageName;
-
- @Rule
- public ServiceTestRule mServiceTestRule;
-
- @Before
- public void setUp() throws Exception {
- InstrumentationRegistry
- .getInstrumentation()
- .getUiAutomation()
- .adoptShellPermissionIdentity(
- Manifest.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE);
-
- mServiceTestRule = new ServiceTestRule();
- mService = IExplicitHealthCheckService.Stub.asInterface(
- mServiceTestRule.bindService(getExtServiceIntent()));
- mNetworkStackPackageName = getNetworkStackPackage();
- assumeFalse(mNetworkStackPackageName == null);
- }
-
- @After
- public void tearDown() {
- InstrumentationRegistry
- .getInstrumentation()
- .getUiAutomation()
- .dropShellPermissionIdentity();
- }
-
- @Test
- public void testHealthCheckSupportedPackage() throws Exception {
- List<PackageConfig> supportedPackages = new ArrayList<>();
- CountDownLatch latch = new CountDownLatch(1);
-
- mService.getSupportedPackages(new RemoteCallback(result -> {
- supportedPackages.addAll(result.getParcelableArrayList(EXTRA_SUPPORTED_PACKAGES));
- latch.countDown();
- }));
- latch.await();
-
- // TODO: Support DeviceConfig changes for the health check timeout
- assertThat(supportedPackages).hasSize(1);
- assertThat(supportedPackages.get(0).getPackageName())
- .isEqualTo(mNetworkStackPackageName);
- assertThat(supportedPackages.get(0).getHealthCheckTimeoutMillis())
- .isEqualTo(ExplicitHealthCheckServiceImpl.DEFAULT_REQUEST_TIMEOUT_MILLIS);
- }
-
- @Test
- public void testHealthCheckRequests() throws Exception {
- List<String> requestedPackages = new ArrayList<>();
- CountDownLatch latch1 = new CountDownLatch(1);
- CountDownLatch latch2 = new CountDownLatch(1);
- CountDownLatch latch3 = new CountDownLatch(1);
-
- // Initially, no health checks requested
- mService.getRequestedPackages(new RemoteCallback(result -> {
- requestedPackages.addAll(result.getParcelableArrayList(EXTRA_REQUESTED_PACKAGES));
- latch1.countDown();
- }));
-
- // Verify that no health checks requested
- latch1.await();
- assertThat(requestedPackages).isEmpty();
-
- // Then request health check
- mService.request(mNetworkStackPackageName);
-
- // Verify that health check is requested for network stack
- mService.getRequestedPackages(new RemoteCallback(result -> {
- requestedPackages.addAll(result.getParcelableArrayList(EXTRA_REQUESTED_PACKAGES));
- latch2.countDown();
- }));
- latch2.await();
- assertThat(requestedPackages).hasSize(1);
- assertThat(requestedPackages.get(0)).isEqualTo(mNetworkStackPackageName);
-
- // Then cancel health check
- requestedPackages.clear();
- mService.cancel(mNetworkStackPackageName);
-
- // Verify that health check is cancelled for network stack
- mService.getRequestedPackages(new RemoteCallback(result -> {
- requestedPackages.addAll(result.getParcelableArrayList(EXTRA_REQUESTED_PACKAGES));
- latch3.countDown();
- }));
- latch3.await();
- assertThat(requestedPackages).isEmpty();
- }
-
- private String getNetworkStackPackage() {
- Intent intent = new Intent(NETWORK_STACK_CONNECTOR_CLASS);
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- if (comp != null) {
- return comp.getPackageName();
- } else {
- // On Go devices, or any device that does not ship the network stack module.
- // The network stack will live in system_server process, so no need to monitor.
- return null;
- }
- }
-
- private Intent getExtServiceIntent() {
- ComponentName component = getExtServiceComponentNameLocked();
- if (component == null) {
- fail("Health check service not found");
- }
- Intent intent = new Intent();
- intent.setComponent(component);
- return intent;
- }
-
- private ComponentName getExtServiceComponentNameLocked() {
- ServiceInfo serviceInfo = getExtServiceInfoLocked();
- if (serviceInfo == null) {
- return null;
- }
-
- final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
- if (!Manifest.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE
- .equals(serviceInfo.permission)) {
- return null;
- }
- return name;
- }
-
- private ServiceInfo getExtServiceInfoLocked() {
- final String packageName =
- mContext.getPackageManager().getServicesSystemSharedLibraryPackageName();
- if (packageName == null) {
- return null;
- }
-
- final Intent intent = new Intent(ExplicitHealthCheckService.SERVICE_INTERFACE);
- intent.setPackage(packageName);
- final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
- PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
- if (resolveInfo == null || resolveInfo.serviceInfo == null) {
- return null;
- }
- return resolveInfo.serviceInfo;
- }
-}
diff --git a/packages/NetworkPermissionConfig/Android.bp b/packages/NetworkPermissionConfig/Android.bp
deleted file mode 100644
index 6e50459..0000000
--- a/packages/NetworkPermissionConfig/Android.bp
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-// Copyright (C) 2019 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.
-//
-
-java_defaults {
- name: "NetworkPermissionConfigDefaults",
- // TODO: mark app as hasCode=false in manifest once soong stops complaining about apps without
- // a classes.dex.
- srcs: ["src/**/*.java"],
- platform_apis: true,
- min_sdk_version: "28",
- privileged: true,
- manifest: "AndroidManifest.xml",
-}
-
-// Stub APK to define permissions for NetworkStack
-android_app {
- name: "NetworkPermissionConfig",
- defaults: ["NetworkPermissionConfigDefaults"],
- certificate: "networkstack",
-}
-
-// Alternative stub APK signed with platform certificate. To use with InProcessNetworkStack.
-android_app {
- name: "PlatformNetworkPermissionConfig",
- defaults: ["NetworkPermissionConfigDefaults"],
- certificate: "platform",
- overrides: ["NetworkPermissionConfig"],
-}
diff --git a/packages/NetworkPermissionConfig/AndroidManifest.xml b/packages/NetworkPermissionConfig/AndroidManifest.xml
deleted file mode 100644
index 496fa4d..0000000
--- a/packages/NetworkPermissionConfig/AndroidManifest.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright (C) 2019 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.networkstack.permissionconfig"
- android:sharedUserId="android.uid.networkstack"
- android:versionCode="210000000"
- android:versionName="Q-initial">
- <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
- <!--
- This package only exists to define the below permissions, and enforce that they are only
- granted to apps sharing the same signature.
- Permissions defined here are intended to be used only by the NetworkStack: both
- NetworkStack and this stub APK are to be signed with a dedicated certificate to ensure
- that, with the below permissions being signature permissions.
-
- This APK *must* be installed, even if the NetworkStack app is not installed, because otherwise,
- any application will be able to define this permission and the system will give that application
- full access to the network stack.
- -->
- <permission android:name="android.permission.MAINLINE_NETWORK_STACK"
- android:protectionLevel="signature"/>
-
- <application android:name="com.android.server.NetworkPermissionConfig"/>
-</manifest>
diff --git a/packages/NetworkPermissionConfig/src/com/android/server/NetworkPermissionConfig.java b/packages/NetworkPermissionConfig/src/com/android/server/NetworkPermissionConfig.java
deleted file mode 100644
index c904e23..0000000
--- a/packages/NetworkPermissionConfig/src/com/android/server/NetworkPermissionConfig.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.app.Application;
-
-/**
- * Empty application for NetworkPermissionConfig that only exists because
- * soong builds complain if APKs have no source file.
- */
-public class NetworkPermissionConfig extends Application {
-}
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
deleted file mode 100644
index c013b8c..0000000
--- a/packages/NetworkStack/Android.bp
+++ /dev/null
@@ -1,134 +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.
-//
-
-java_library {
- name: "captiveportal-lib",
- srcs: ["common/**/*.java"],
- libs: [
- "androidx.annotation_annotation",
- ],
- sdk_version: "system_current",
-}
-
-java_defaults {
- name: "NetworkStackCommon",
- sdk_version: "system_current",
- min_sdk_version: "28",
-}
-
-// Library including the network stack, used to compile both variants of the network stack
-android_library {
- name: "NetworkStackBase",
- defaults: ["NetworkStackCommon"],
- srcs: [
- "src/**/*.java",
- ":framework-networkstack-shared-srcs",
- ":services-networkstack-shared-srcs",
- ":statslog-networkstack-java-gen",
- ],
- static_libs: [
- "androidx.annotation_annotation",
- "ipmemorystore-client",
- "netd_aidl_interface-V2-java",
- "networkstack-aidl-interfaces-V3-java",
- "datastallprotosnano",
- "networkstackprotosnano",
- "captiveportal-lib",
- ],
- manifest: "AndroidManifestBase.xml",
-}
-
-cc_library_shared {
- name: "libnetworkstackutilsjni",
- srcs: [
- "jni/network_stack_utils_jni.cpp"
- ],
- sdk_version: "current",
- shared_libs: [
- "liblog",
- "libnativehelper_compat_libc++",
- ],
-
- // We cannot use plain "libc++" here to link libc++ dynamically because it results in:
- // java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++_shared.so" not found
- // even if "libc++" is added into jni_libs below. Adding "libc++_shared" into jni_libs doesn't
- // build because soong complains of:
- // module NetworkStack missing dependencies: libc++_shared
- //
- // So, link libc++ statically. This means that we also need to ensure that all the C++ libraries
- // we depend on do not dynamically link libc++. This is currently the case, because liblog is
- // C-only and libnativehelper_compat_libc also uses stl: "c++_static".
- //
- // TODO: find a better solution for this in R.
- stl: "c++_static",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-unused-parameter",
- ],
-}
-
-java_defaults {
- name: "NetworkStackAppCommon",
- defaults: ["NetworkStackCommon"],
- privileged: true,
- static_libs: [
- "NetworkStackBase",
- ],
- jni_libs: [
- "libnativehelper_compat_libc++",
- "libnetworkstackutilsjni",
- ],
- // Resources already included in NetworkStackBase
- resource_dirs: [],
- jarjar_rules: "jarjar-rules-shared.txt",
- optimize: {
- proguard_flags_files: ["proguard.flags"],
- },
-}
-
-// Non-updatable network stack running in the system server process for devices not using the module
-android_app {
- name: "InProcessNetworkStack",
- defaults: ["NetworkStackAppCommon"],
- certificate: "platform",
- manifest: "AndroidManifest_InProcess.xml",
- // InProcessNetworkStack is a replacement for NetworkStack
- overrides: ["NetworkStack"],
- // The permission configuration *must* be included to ensure security of the device
- // The InProcessNetworkStack goes together with the PlatformCaptivePortalLogin, which replaces
- // the default CaptivePortalLogin.
- required: ["PlatformNetworkPermissionConfig", "PlatformCaptivePortalLogin"],
-}
-
-// Updatable network stack packaged as an application
-android_app {
- name: "NetworkStack",
- defaults: ["NetworkStackAppCommon"],
- certificate: "networkstack",
- manifest: "AndroidManifest.xml",
- use_embedded_native_libs: true,
- // The permission configuration *must* be included to ensure security of the device
- required: ["NetworkPermissionConfig"],
-}
-
-genrule {
- name: "statslog-networkstack-java-gen",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --java $(out) --module network_stack" +
- " --javaPackage com.android.networkstack.metrics --javaClass NetworkStackStatsLog",
- out: ["com/android/networkstack/metrics/NetworkStackStatsLog.java"],
-}
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
deleted file mode 100644
index 6166c66..0000000
--- a/packages/NetworkStack/AndroidManifest.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright (C) 2019 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.networkstack"
- android:sharedUserId="android.uid.networkstack"
- android:versionCode="220000000"
- android:versionName="29 system image"
->
-
- <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
-
- <!-- Permissions must be defined here, and not in the base manifest, as the network stack
- running in the system server process does not need any permission, and having privileged
- permissions added would cause crashes on startup unless they are also added to the
- privileged permissions whitelist for that package. -->
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
- <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
- <!-- Send latency broadcast as current user -->
- <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
- <uses-permission android:name="android.permission.WAKE_LOCK" />
- <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
- <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
- <!-- Signature permission defined in NetworkStackStub -->
- <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" />
- <application
- android:extractNativeLibs="false"
- android:persistent="true">
- <service android:name="com.android.server.NetworkStackService">
- <intent-filter>
- <action android:name="android.net.INetworkStackConnector"/>
- </intent-filter>
- </service>
- <service android:name="com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService"
- android:permission="android.permission.BIND_JOB_SERVICE" >
- </service>
- </application>
-</manifest>
diff --git a/packages/NetworkStack/AndroidManifestBase.xml b/packages/NetworkStack/AndroidManifestBase.xml
deleted file mode 100644
index 69a4da4..0000000
--- a/packages/NetworkStack/AndroidManifestBase.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright (C) 2019 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.networkstack"
- android:versionCode="11"
- android:versionName="Q-initial">
- <application
- android:label="NetworkStack"
- android:defaultToDeviceProtectedStorage="true"
- android:directBootAware="true"
- android:usesCleartextTraffic="true">
- </application>
-</manifest>
diff --git a/packages/NetworkStack/AndroidManifest_InProcess.xml b/packages/NetworkStack/AndroidManifest_InProcess.xml
deleted file mode 100644
index 2778a2a..0000000
--- a/packages/NetworkStack/AndroidManifest_InProcess.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright (C) 2019 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.networkstack.inprocess"
- android:sharedUserId="android.uid.system"
- android:process="system">
- <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
- <application>
- <service android:name="com.android.server.NetworkStackService" android:process="system">
- <intent-filter>
- <action android:name="android.net.INetworkStackConnector.InProcess"/>
- </intent-filter>
- </service>
- <service android:name="com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService"
- android:process="system"
- android:permission="android.permission.BIND_JOB_SERVICE" >
- </service>
- </application>
-</manifest>
diff --git a/packages/NetworkStack/OWNERS b/packages/NetworkStack/OWNERS
deleted file mode 100644
index 0e1e65d..0000000
--- a/packages/NetworkStack/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-codewiz@google.com
-jchalard@google.com
-junyulai@google.com
-lorenzo@google.com
-reminv@google.com
-satk@google.com
diff --git a/packages/NetworkStack/TEST_MAPPING b/packages/NetworkStack/TEST_MAPPING
deleted file mode 100644
index fe9731fe..0000000
--- a/packages/NetworkStack/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "presubmit": [
- {
- "name": "NetworkStackTests"
- }
- ]
-}
\ No newline at end of file
diff --git a/packages/NetworkStack/common/CaptivePortalProbeResult.java b/packages/NetworkStack/common/CaptivePortalProbeResult.java
deleted file mode 100644
index 48cd48b..0000000
--- a/packages/NetworkStack/common/CaptivePortalProbeResult.java
+++ /dev/null
@@ -1,88 +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 android.net.captiveportal;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * Result of calling isCaptivePortal().
- * @hide
- */
-public final class CaptivePortalProbeResult {
- public static final int SUCCESS_CODE = 204;
- public static final int FAILED_CODE = 599;
- public static final int PORTAL_CODE = 302;
- // Set partial connectivity http response code to -1 to prevent conflict with the other http
- // response codes. Besides the default http response code of probe result is set as 599 in
- // NetworkMonitor#sendParallelHttpProbes(), so response code will be set as -1 only when
- // NetworkMonitor detects partial connectivity.
- /**
- * @hide
- */
- public static final int PARTIAL_CODE = -1;
-
- @NonNull
- public static final CaptivePortalProbeResult FAILED = new CaptivePortalProbeResult(FAILED_CODE);
- @NonNull
- public static final CaptivePortalProbeResult SUCCESS =
- new CaptivePortalProbeResult(SUCCESS_CODE);
- public static final CaptivePortalProbeResult PARTIAL =
- new CaptivePortalProbeResult(PARTIAL_CODE);
-
- private final int mHttpResponseCode; // HTTP response code returned from Internet probe.
- @Nullable
- public final String redirectUrl; // Redirect destination returned from Internet probe.
- @Nullable
- public final String detectUrl; // URL where a 204 response code indicates
- // captive portal has been appeased.
- @Nullable
- public final CaptivePortalProbeSpec probeSpec;
-
- public CaptivePortalProbeResult(int httpResponseCode) {
- this(httpResponseCode, null, null);
- }
-
- public CaptivePortalProbeResult(int httpResponseCode, @Nullable String redirectUrl,
- @Nullable String detectUrl) {
- this(httpResponseCode, redirectUrl, detectUrl, null);
- }
-
- public CaptivePortalProbeResult(int httpResponseCode, @Nullable String redirectUrl,
- @Nullable String detectUrl, @Nullable CaptivePortalProbeSpec probeSpec) {
- mHttpResponseCode = httpResponseCode;
- this.redirectUrl = redirectUrl;
- this.detectUrl = detectUrl;
- this.probeSpec = probeSpec;
- }
-
- public boolean isSuccessful() {
- return mHttpResponseCode == SUCCESS_CODE;
- }
-
- public boolean isPortal() {
- return !isSuccessful() && (mHttpResponseCode >= 200) && (mHttpResponseCode <= 399);
- }
-
- public boolean isFailed() {
- return !isSuccessful() && !isPortal();
- }
-
- public boolean isPartialConnectivity() {
- return mHttpResponseCode == PARTIAL_CODE;
- }
-}
diff --git a/packages/NetworkStack/common/CaptivePortalProbeSpec.java b/packages/NetworkStack/common/CaptivePortalProbeSpec.java
deleted file mode 100644
index bf983a5..0000000
--- a/packages/NetworkStack/common/CaptivePortalProbeSpec.java
+++ /dev/null
@@ -1,195 +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 android.net.captiveportal;
-
-import static android.net.captiveportal.CaptivePortalProbeResult.PORTAL_CODE;
-import static android.net.captiveportal.CaptivePortalProbeResult.SUCCESS_CODE;
-
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.regex.Pattern;
-import java.util.regex.PatternSyntaxException;
-
-/** @hide */
-public abstract class CaptivePortalProbeSpec {
- private static final String TAG = CaptivePortalProbeSpec.class.getSimpleName();
- private static final String REGEX_SEPARATOR = "@@/@@";
- private static final String SPEC_SEPARATOR = "@@,@@";
-
- private final String mEncodedSpec;
- private final URL mUrl;
-
- CaptivePortalProbeSpec(@NonNull String encodedSpec, @NonNull URL url) {
- mEncodedSpec = checkNotNull(encodedSpec);
- mUrl = checkNotNull(url);
- }
-
- /**
- * Parse a {@link CaptivePortalProbeSpec} from a {@link String}.
- *
- * <p>The valid format is a URL followed by two regular expressions, each separated by "@@/@@".
- * @throws MalformedURLException The URL has invalid format for {@link URL#URL(String)}.
- * @throws ParseException The string is empty, does not match the above format, or a regular
- * expression is invalid for {@link Pattern#compile(String)}.
- * @hide
- */
- @VisibleForTesting
- @NonNull
- public static CaptivePortalProbeSpec parseSpec(@NonNull String spec) throws ParseException,
- MalformedURLException {
- if (TextUtils.isEmpty(spec)) {
- throw new ParseException("Empty probe spec", 0 /* errorOffset */);
- }
-
- String[] splits = TextUtils.split(spec, REGEX_SEPARATOR);
- if (splits.length != 3) {
- throw new ParseException("Probe spec does not have 3 parts", 0 /* errorOffset */);
- }
-
- final int statusRegexPos = splits[0].length() + REGEX_SEPARATOR.length();
- final int locationRegexPos = statusRegexPos + splits[1].length() + REGEX_SEPARATOR.length();
- final Pattern statusRegex = parsePatternIfNonEmpty(splits[1], statusRegexPos);
- final Pattern locationRegex = parsePatternIfNonEmpty(splits[2], locationRegexPos);
-
- return new RegexMatchProbeSpec(spec, new URL(splits[0]), statusRegex, locationRegex);
- }
-
- @Nullable
- private static Pattern parsePatternIfNonEmpty(@Nullable String pattern, int pos)
- throws ParseException {
- if (TextUtils.isEmpty(pattern)) {
- return null;
- }
- try {
- return Pattern.compile(pattern);
- } catch (PatternSyntaxException e) {
- throw new ParseException(
- String.format("Invalid status pattern [%s]: %s", pattern, e),
- pos /* errorOffset */);
- }
- }
-
- /**
- * Parse a {@link CaptivePortalProbeSpec} from a {@link String}, or return a fallback spec
- * based on the status code of the provided URL if the spec cannot be parsed.
- */
- @Nullable
- public static CaptivePortalProbeSpec parseSpecOrNull(@Nullable String spec) {
- if (spec != null) {
- try {
- return parseSpec(spec);
- } catch (ParseException | MalformedURLException e) {
- Log.e(TAG, "Invalid probe spec: " + spec, e);
- // Fall through
- }
- }
- return null;
- }
-
- /**
- * Parse a config String to build an array of {@link CaptivePortalProbeSpec}.
- *
- * <p>Each spec is separated by @@,@@ and follows the format for {@link #parseSpec(String)}.
- * <p>This method does not throw but ignores any entry that could not be parsed.
- */
- @NonNull
- public static Collection<CaptivePortalProbeSpec> parseCaptivePortalProbeSpecs(
- @NonNull String settingsVal) {
- List<CaptivePortalProbeSpec> specs = new ArrayList<>();
- if (settingsVal != null) {
- for (String spec : TextUtils.split(settingsVal, SPEC_SEPARATOR)) {
- try {
- specs.add(parseSpec(spec));
- } catch (ParseException | MalformedURLException e) {
- Log.e(TAG, "Invalid probe spec: " + spec, e);
- }
- }
- }
-
- if (specs.isEmpty()) {
- Log.e(TAG, String.format("could not create any validation spec from %s", settingsVal));
- }
- return specs;
- }
-
- /**
- * Get the probe result from HTTP status and location header.
- */
- @NonNull
- public abstract CaptivePortalProbeResult getResult(int status, @Nullable String locationHeader);
-
- @NonNull
- public String getEncodedSpec() {
- return mEncodedSpec;
- }
-
- @NonNull
- public URL getUrl() {
- return mUrl;
- }
-
- /**
- * Implementation of {@link CaptivePortalProbeSpec} that is based on configurable regular
- * expressions for the HTTP status code and location header (if any). Matches indicate that
- * the page is not a portal.
- * This probe cannot fail: it always returns SUCCESS_CODE or PORTAL_CODE
- */
- private static class RegexMatchProbeSpec extends CaptivePortalProbeSpec {
- @Nullable
- final Pattern mStatusRegex;
- @Nullable
- final Pattern mLocationHeaderRegex;
-
- RegexMatchProbeSpec(
- String spec, URL url, Pattern statusRegex, Pattern locationHeaderRegex) {
- super(spec, url);
- mStatusRegex = statusRegex;
- mLocationHeaderRegex = locationHeaderRegex;
- }
-
- @Override
- public CaptivePortalProbeResult getResult(int status, String locationHeader) {
- final boolean statusMatch = safeMatch(String.valueOf(status), mStatusRegex);
- final boolean locationMatch = safeMatch(locationHeader, mLocationHeaderRegex);
- final int returnCode = statusMatch && locationMatch ? SUCCESS_CODE : PORTAL_CODE;
- return new CaptivePortalProbeResult(
- returnCode, locationHeader, getUrl().toString(), this);
- }
- }
-
- private static boolean safeMatch(@Nullable String value, @Nullable Pattern pattern) {
- // No value is a match ("no location header" passes the location rule for non-redirects)
- return pattern == null || TextUtils.isEmpty(value) || pattern.matcher(value).matches();
- }
-
- // Throws NullPointerException if the input is null.
- private static <T> T checkNotNull(T object) {
- if (object == null) throw new NullPointerException();
- return object;
- }
-}
diff --git a/packages/NetworkStack/jarjar-rules-shared.txt b/packages/NetworkStack/jarjar-rules-shared.txt
deleted file mode 100644
index 7346b1a..0000000
--- a/packages/NetworkStack/jarjar-rules-shared.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-rule com.android.internal.util.** android.net.networkstack.util.@1
-
-rule android.net.shared.Inet4AddressUtils* android.net.networkstack.shared.Inet4AddressUtils@1
-rule android.net.shared.InetAddressUtils* android.net.networkstack.shared.InetAddressUtils@1
-
-# Ignore DhcpResultsParcelable, but jarjar DhcpResults
-# TODO: move DhcpResults into services.net and delete from here
-rule android.net.DhcpResultsParcelable* @0
-rule android.net.DhcpResults* android.net.networkstack.DhcpResults@1
-rule android.net.LocalLog* android.net.networkstack.LocalLog@1
diff --git a/packages/NetworkStack/jni/network_stack_utils_jni.cpp b/packages/NetworkStack/jni/network_stack_utils_jni.cpp
deleted file mode 100644
index f2ba575..0000000
--- a/packages/NetworkStack/jni/network_stack_utils_jni.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright 2019, 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 "NetworkStackUtils-JNI"
-
-#include <errno.h>
-#include <jni.h>
-#include <linux/filter.h>
-#include <linux/if_arp.h>
-#include <net/if.h>
-#include <netinet/ether.h>
-#include <netinet/icmp6.h>
-#include <netinet/ip.h>
-#include <netinet/ip6.h>
-#include <netinet/udp.h>
-#include <stdlib.h>
-
-#include <string>
-
-#include <nativehelper/JNIHelp.h>
-#include <android/log.h>
-
-namespace android {
-constexpr const char NETWORKSTACKUTILS_PKG_NAME[] = "android/net/util/NetworkStackUtils";
-
-static const uint32_t kEtherTypeOffset = offsetof(ether_header, ether_type);
-static const uint32_t kEtherHeaderLen = sizeof(ether_header);
-static const uint32_t kIPv4Protocol = kEtherHeaderLen + offsetof(iphdr, protocol);
-static const uint32_t kIPv4FlagsOffset = kEtherHeaderLen + offsetof(iphdr, frag_off);
-static const uint32_t kIPv6NextHeader = kEtherHeaderLen + offsetof(ip6_hdr, ip6_nxt);
-static const uint32_t kIPv6PayloadStart = kEtherHeaderLen + sizeof(ip6_hdr);
-static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type);
-static const uint32_t kUDPSrcPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, source);
-static const uint32_t kUDPDstPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, dest);
-static const uint16_t kDhcpClientPort = 68;
-
-static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst) {
- if (env->GetArrayLength(addr) != len) {
- return false;
- }
- env->GetByteArrayRegion(addr, 0, len, reinterpret_cast<jbyte*>(dst));
- return true;
-}
-
-static void network_stack_utils_addArpEntry(JNIEnv *env, jobject thiz, jbyteArray ethAddr,
- jbyteArray ipv4Addr, jstring ifname, jobject javaFd) {
- arpreq req = {};
- sockaddr_in& netAddrStruct = *reinterpret_cast<sockaddr_in*>(&req.arp_pa);
- sockaddr& ethAddrStruct = req.arp_ha;
-
- ethAddrStruct.sa_family = ARPHRD_ETHER;
- if (!checkLenAndCopy(env, ethAddr, ETH_ALEN, ethAddrStruct.sa_data)) {
- jniThrowException(env, "java/io/IOException", "Invalid ethAddr length");
- return;
- }
-
- netAddrStruct.sin_family = AF_INET;
- if (!checkLenAndCopy(env, ipv4Addr, sizeof(in_addr), &netAddrStruct.sin_addr)) {
- jniThrowException(env, "java/io/IOException", "Invalid ipv4Addr length");
- return;
- }
-
- int ifLen = env->GetStringLength(ifname);
- // IFNAMSIZ includes the terminating NULL character
- if (ifLen >= IFNAMSIZ) {
- jniThrowException(env, "java/io/IOException", "ifname too long");
- return;
- }
- env->GetStringUTFRegion(ifname, 0, ifLen, req.arp_dev);
-
- req.arp_flags = ATF_COM; // Completed entry (ha valid)
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- if (fd < 0) {
- jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor");
- return;
- }
- // See also: man 7 arp
- if (ioctl(fd, SIOCSARP, &req)) {
- jniThrowExceptionFmt(env, "java/io/IOException", "ioctl error: %s", strerror(errno));
- return;
- }
-}
-
-static void network_stack_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd) {
- static sock_filter filter_code[] = {
- // Check the protocol is UDP.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 6),
-
- // Check this is not a fragment.
- BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset),
- BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_OFFMASK, 4, 0),
-
- // Get the IP header length.
- BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen),
-
- // Check the destination port.
- BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPDstPortIndirectOffset),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 0, 1),
-
- // Accept or reject.
- BPF_STMT(BPF_RET | BPF_K, 0xffff),
- BPF_STMT(BPF_RET | BPF_K, 0)
- };
- static const sock_fprog filter = {
- sizeof(filter_code) / sizeof(filter_code[0]),
- filter_code,
- };
-
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
- }
-}
-
-static void network_stack_utils_attachRaFilter(JNIEnv *env, jobject clazz, jobject javaFd,
- jint hardwareAddressType) {
- if (hardwareAddressType != ARPHRD_ETHER) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "attachRaFilter only supports ARPHRD_ETHER");
- return;
- }
-
- static sock_filter filter_code[] = {
- // Check IPv6 Next Header is ICMPv6.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeader),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
-
- // Check ICMPv6 type is Router Advertisement.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 0, 1),
-
- // Accept or reject.
- BPF_STMT(BPF_RET | BPF_K, 0xffff),
- BPF_STMT(BPF_RET | BPF_K, 0)
- };
- static const sock_fprog filter = {
- sizeof(filter_code) / sizeof(filter_code[0]),
- filter_code,
- };
-
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
- }
-}
-
-// TODO: Move all this filter code into libnetutils.
-static void network_stack_utils_attachControlPacketFilter(
- JNIEnv *env, jobject clazz, jobject javaFd, jint hardwareAddressType) {
- if (hardwareAddressType != ARPHRD_ETHER) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "attachControlPacketFilter only supports ARPHRD_ETHER");
- return;
- }
-
- // Capture all:
- // - ARPs
- // - DHCPv4 packets
- // - Router Advertisements & Solicitations
- // - Neighbor Advertisements & Solicitations
- //
- // tcpdump:
- // arp or
- // '(ip and udp port 68)' or
- // '(icmp6 and ip6[40] >= 133 and ip6[40] <= 136)'
- static sock_filter filter_code[] = {
- // Load the link layer next payload field.
- BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kEtherTypeOffset),
-
- // Accept all ARP.
- // TODO: Figure out how to better filter ARPs on noisy networks.
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_ARP, 16, 0),
-
- // If IPv4:
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IP, 0, 9),
-
- // Check the protocol is UDP.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 14),
-
- // Check this is not a fragment.
- BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset),
- BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_OFFMASK, 12, 0),
-
- // Get the IP header length.
- BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen),
-
- // Check the source port.
- BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPSrcPortIndirectOffset),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 8, 0),
-
- // Check the destination port.
- BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPDstPortIndirectOffset),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 6, 7),
-
- // IPv6 ...
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IPV6, 0, 6),
- // ... check IPv6 Next Header is ICMPv6 (ignore fragments), ...
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeader),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 4),
- // ... and check the ICMPv6 type is one of RS/RA/NS/NA.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
- BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, ND_ROUTER_SOLICIT, 0, 2),
- BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, ND_NEIGHBOR_ADVERT, 1, 0),
-
- // Accept or reject.
- BPF_STMT(BPF_RET | BPF_K, 0xffff),
- BPF_STMT(BPF_RET | BPF_K, 0)
- };
- static const sock_fprog filter = {
- sizeof(filter_code) / sizeof(filter_code[0]),
- filter_code,
- };
-
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
- }
-}
-
-/*
- * JNI registration.
- */
-static const JNINativeMethod gNetworkStackUtilsMethods[] = {
- /* name, signature, funcPtr */
- { "addArpEntry", "([B[BLjava/lang/String;Ljava/io/FileDescriptor;)V", (void*) network_stack_utils_addArpEntry },
- { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) network_stack_utils_attachDhcpFilter },
- { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) network_stack_utils_attachRaFilter },
- { "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) network_stack_utils_attachControlPacketFilter },
-};
-
-extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
- JNIEnv *env;
- if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
- __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: GetEnv failed");
- return JNI_ERR;
- }
-
- if (jniRegisterNativeMethods(env, NETWORKSTACKUTILS_PKG_NAME,
- gNetworkStackUtilsMethods, NELEM(gNetworkStackUtilsMethods)) < 0) {
- return JNI_ERR;
- }
-
- return JNI_VERSION_1_6;
-
-}
-}; // namespace android
diff --git a/packages/NetworkStack/proguard.flags b/packages/NetworkStack/proguard.flags
deleted file mode 100644
index c60f6c3..0000000
--- a/packages/NetworkStack/proguard.flags
+++ /dev/null
@@ -1,9 +0,0 @@
--keepclassmembers class android.net.ip.IpClient {
- static final int CMD_*;
- static final int EVENT_*;
-}
-
--keepclassmembers class android.net.dhcp.DhcpClient {
- static final int CMD_*;
- static final int EVENT_*;
-}
diff --git a/packages/NetworkStack/res/values-mcc460/config.xml b/packages/NetworkStack/res/values-mcc460/config.xml
deleted file mode 100644
index fd4a848..0000000
--- a/packages/NetworkStack/res/values-mcc460/config.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <!-- Network validation URL configuration for devices using a Chinese SIM (MCC 460).
- The below URLs are often whitelisted by captive portals, so they should not be used in the
- general case as this could degrade the user experience (portals not detected properly).
- However in China the default URLs are not accessible in general. The below alternatives
- should allow users to connect to local networks normally. -->
- <string name="default_captive_portal_https_url" translatable="false">https://connectivitycheck.gstatic.com/generate_204</string>
- <string-array name="default_captive_portal_fallback_urls" translatable="false">
- <item>http://www.googleapis.cn/generate_204</item>
- </string-array>
-</resources>
diff --git a/packages/NetworkStack/res/values/config.xml b/packages/NetworkStack/res/values/config.xml
deleted file mode 100644
index 478ed6b..0000000
--- a/packages/NetworkStack/res/values/config.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <!--
- OEMs that wish to change the below settings must do so via a runtime resource overlay package
- and *NOT* by changing this file. This file is part of the NetworkStack mainline module.
- The overlays must apply to the config_* values, not the default_* values. The default_*
- values are meant to be the default when no other configuration is specified.
- -->
-
- <!-- DNS probe timeout for network validation. Enough for 3 DNS queries 5 seconds apart. -->
- <integer name="default_captive_portal_dns_probe_timeout">12500</integer>
-
- <!-- HTTP URL for network validation, to use for detecting captive portals. -->
- <string name="default_captive_portal_http_url" translatable="false">http://connectivitycheck.gstatic.com/generate_204</string>
-
- <!-- HTTPS URL for network validation, to use for confirming internet connectivity. -->
- <string name="default_captive_portal_https_url" translatable="false">https://www.google.com/generate_204</string>
-
- <!-- List of fallback URLs to use for detecting captive portals. -->
- <string-array name="default_captive_portal_fallback_urls" translatable="false">
- <item>http://www.google.com/gen_204</item>
- <item>http://play.googleapis.com/generate_204</item>
- </string-array>
-
- <!-- List of fallback probe specs to use for detecting captive portals.
- This is an alternative to fallback URLs that provides more flexibility on detection rules.
- Empty, so unused by default. -->
- <string-array name="default_captive_portal_fallback_probe_specs" translatable="false">
- </string-array>
-
- <!-- Configuration hooks for the above settings.
- Empty by default but may be overridden by RROs. -->
- <integer name="config_captive_portal_dns_probe_timeout"></integer>
- <!--suppress CheckTagEmptyBody: overlayable resource to use as configuration hook -->
- <string name="config_captive_portal_http_url" translatable="false"></string>
- <!--suppress CheckTagEmptyBody: overlayable resource to use as configuration hook -->
- <string name="config_captive_portal_https_url" translatable="false"></string>
- <string-array name="config_captive_portal_fallback_urls" translatable="false">
- </string-array>
- <string-array name="config_captive_portal_fallback_probe_specs" translatable="false">
- </string-array>
-
- <!-- Customized default DNS Servers address. -->
- <string-array name="config_default_dns_servers" translatable="false">
- </string-array>
-</resources>
diff --git a/packages/NetworkStack/res/values/overlayable.xml b/packages/NetworkStack/res/values/overlayable.xml
deleted file mode 100644
index b9d5337..0000000
--- a/packages/NetworkStack/res/values/overlayable.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<!-- Copyright (C) 2019 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:android="http://schemas.android.com/apk/res/android">
- <overlayable name="NetworkStackConfig">
- <policy type="product|system|vendor">
- <!-- Configuration values for NetworkMonitor -->
- <item type="integer" name="config_captive_portal_dns_probe_timeout"/>
- <item type="string" name="config_captive_portal_http_url"/>
- <item type="string" name="config_captive_portal_https_url"/>
- <item type="array" name="config_captive_portal_fallback_urls"/>
- <!-- Configuration value for DhcpResults -->
- <item type="array" name="config_default_dns_servers"/>
- </policy>
- </overlayable>
-</resources>
diff --git a/packages/NetworkStack/src/android/net/NetworkStackIpMemoryStore.java b/packages/NetworkStack/src/android/net/NetworkStackIpMemoryStore.java
deleted file mode 100644
index 41715b2..0000000
--- a/packages/NetworkStack/src/android/net/NetworkStackIpMemoryStore.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.annotation.NonNull;
-import android.content.Context;
-
-import java.util.concurrent.ExecutionException;
-import java.util.function.Consumer;
-
-/**
- * service used to communicate with the ip memory store service in network stack,
- * which is running in the same module.
- * @see com.android.server.connectivity.ipmemorystore.IpMemoryStoreService
- * @hide
- */
-public class NetworkStackIpMemoryStore extends IpMemoryStoreClient {
- @NonNull private final IIpMemoryStore mService;
-
- public NetworkStackIpMemoryStore(@NonNull final Context context,
- @NonNull final IIpMemoryStore service) {
- super(context);
- mService = service;
- }
-
- @Override
- protected void runWhenServiceReady(Consumer<IIpMemoryStore> cb) throws ExecutionException {
- cb.accept(mService);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/apf/ApfFilter.java b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
deleted file mode 100644
index f054319..0000000
--- a/packages/NetworkStack/src/android/net/apf/ApfFilter.java
+++ /dev/null
@@ -1,1981 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.apf;
-
-import static android.net.util.SocketUtils.makePacketSocketAddress;
-import static android.system.OsConstants.AF_PACKET;
-import static android.system.OsConstants.ARPHRD_ETHER;
-import static android.system.OsConstants.ETH_P_ARP;
-import static android.system.OsConstants.ETH_P_IP;
-import static android.system.OsConstants.ETH_P_IPV6;
-import static android.system.OsConstants.IPPROTO_ICMPV6;
-import static android.system.OsConstants.IPPROTO_TCP;
-import static android.system.OsConstants.IPPROTO_UDP;
-import static android.system.OsConstants.SOCK_RAW;
-
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION;
-
-import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.NattKeepalivePacketDataParcelable;
-import android.net.TcpKeepalivePacketDataParcelable;
-import android.net.apf.ApfGenerator.IllegalInstructionException;
-import android.net.apf.ApfGenerator.Register;
-import android.net.ip.IpClient.IpClientCallbacksWrapper;
-import android.net.metrics.ApfProgramEvent;
-import android.net.metrics.ApfStats;
-import android.net.metrics.IpConnectivityLog;
-import android.net.metrics.RaEvent;
-import android.net.util.InterfaceParams;
-import android.net.util.NetworkStackUtils;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.text.format.DateUtils;
-import android.util.Log;
-import android.util.Pair;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.HexDump;
-import com.android.internal.util.IndentingPrintWriter;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.ArrayList;
-import java.util.Arrays;
-
-/**
- * For networks that support packet filtering via APF programs, {@code ApfFilter}
- * listens for IPv6 ICMPv6 router advertisements (RAs) and generates APF programs to
- * filter out redundant duplicate ones.
- *
- * Threading model:
- * A collection of RAs we've received is kept in mRas. Generating APF programs uses mRas to
- * know what RAs to filter for, thus generating APF programs is dependent on mRas.
- * mRas can be accessed by multiple threads:
- * - ReceiveThread, which listens for RAs and adds them to mRas, and generates APF programs.
- * - callers of:
- * - setMulticastFilter(), which can cause an APF program to be generated.
- * - dump(), which dumps mRas among other things.
- * - shutdown(), which clears mRas.
- * So access to mRas is synchronized.
- *
- * @hide
- */
-public class ApfFilter {
-
- // Helper class for specifying functional filter parameters.
- public static class ApfConfiguration {
- public ApfCapabilities apfCapabilities;
- public boolean multicastFilter;
- public boolean ieee802_3Filter;
- public int[] ethTypeBlackList;
- }
-
- // Enums describing the outcome of receiving an RA packet.
- private static enum ProcessRaResult {
- MATCH, // Received RA matched a known RA
- DROPPED, // Received RA ignored due to MAX_RAS
- PARSE_ERROR, // Received RA could not be parsed
- ZERO_LIFETIME, // Received RA had 0 lifetime
- UPDATE_NEW_RA, // APF program updated for new RA
- UPDATE_EXPIRY // APF program updated for expiry
- }
-
- /**
- * APF packet counters.
- *
- * Packet counters are 32bit big-endian values, and allocated near the end of the APF data
- * buffer, using negative byte offsets, where -4 is equivalent to maximumApfProgramSize - 4,
- * the last writable 32bit word.
- */
- @VisibleForTesting
- public static enum Counter {
- RESERVED_OOB, // Points to offset 0 from the end of the buffer (out-of-bounds)
- TOTAL_PACKETS,
- PASSED_ARP,
- PASSED_DHCP,
- PASSED_IPV4,
- PASSED_IPV6_NON_ICMP,
- PASSED_IPV4_UNICAST,
- PASSED_IPV6_ICMP,
- PASSED_IPV6_UNICAST_NON_ICMP,
- PASSED_ARP_NON_IPV4,
- PASSED_ARP_UNKNOWN,
- PASSED_ARP_UNICAST_REPLY,
- PASSED_NON_IP_UNICAST,
- DROPPED_ETH_BROADCAST,
- DROPPED_RA,
- DROPPED_GARP_REPLY,
- DROPPED_ARP_OTHER_HOST,
- DROPPED_IPV4_L2_BROADCAST,
- DROPPED_IPV4_BROADCAST_ADDR,
- DROPPED_IPV4_BROADCAST_NET,
- DROPPED_IPV4_MULTICAST,
- DROPPED_IPV6_ROUTER_SOLICITATION,
- DROPPED_IPV6_MULTICAST_NA,
- DROPPED_IPV6_MULTICAST,
- DROPPED_IPV6_MULTICAST_PING,
- DROPPED_IPV6_NON_ICMP_MULTICAST,
- DROPPED_802_3_FRAME,
- DROPPED_ETHERTYPE_BLACKLISTED,
- DROPPED_ARP_REPLY_SPA_NO_HOST,
- DROPPED_IPV4_KEEPALIVE_ACK,
- DROPPED_IPV6_KEEPALIVE_ACK,
- DROPPED_IPV4_NATT_KEEPALIVE;
-
- // Returns the negative byte offset from the end of the APF data segment for
- // a given counter.
- public int offset() {
- return - this.ordinal() * 4; // Currently, all counters are 32bit long.
- }
-
- // Returns the total size of the data segment in bytes.
- public static int totalSize() {
- return (Counter.class.getEnumConstants().length - 1) * 4;
- }
- }
-
- /**
- * When APFv4 is supported, loads R1 with the offset of the specified counter.
- */
- private void maybeSetupCounter(ApfGenerator gen, Counter c) {
- if (mApfCapabilities.hasDataAccess()) {
- gen.addLoadImmediate(Register.R1, c.offset());
- }
- }
-
- // When APFv4 is supported, these point to the trampolines generated by emitEpilogue().
- // Otherwise, they're just aliases for PASS_LABEL and DROP_LABEL.
- private final String mCountAndPassLabel;
- private final String mCountAndDropLabel;
-
- // Thread to listen for RAs.
- @VisibleForTesting
- class ReceiveThread extends Thread {
- private final byte[] mPacket = new byte[1514];
- private final FileDescriptor mSocket;
- private final long mStart = SystemClock.elapsedRealtime();
-
- private int mReceivedRas = 0;
- private int mMatchingRas = 0;
- private int mDroppedRas = 0;
- private int mParseErrors = 0;
- private int mZeroLifetimeRas = 0;
- private int mProgramUpdates = 0;
-
- private volatile boolean mStopped;
-
- public ReceiveThread(FileDescriptor socket) {
- mSocket = socket;
- }
-
- public void halt() {
- mStopped = true;
- // Interrupts the read() call the thread is blocked in.
- NetworkStackUtils.closeSocketQuietly(mSocket);
- }
-
- @Override
- public void run() {
- log("begin monitoring");
- while (!mStopped) {
- try {
- int length = Os.read(mSocket, mPacket, 0, mPacket.length);
- updateStats(processRa(mPacket, length));
- } catch (IOException|ErrnoException e) {
- if (!mStopped) {
- Log.e(TAG, "Read error", e);
- }
- }
- }
- logStats();
- }
-
- private void updateStats(ProcessRaResult result) {
- mReceivedRas++;
- switch(result) {
- case MATCH:
- mMatchingRas++;
- return;
- case DROPPED:
- mDroppedRas++;
- return;
- case PARSE_ERROR:
- mParseErrors++;
- return;
- case ZERO_LIFETIME:
- mZeroLifetimeRas++;
- return;
- case UPDATE_EXPIRY:
- mMatchingRas++;
- mProgramUpdates++;
- return;
- case UPDATE_NEW_RA:
- mProgramUpdates++;
- return;
- }
- }
-
- private void logStats() {
- final long nowMs = SystemClock.elapsedRealtime();
- synchronized (this) {
- final ApfStats stats = new ApfStats.Builder()
- .setReceivedRas(mReceivedRas)
- .setMatchingRas(mMatchingRas)
- .setDroppedRas(mDroppedRas)
- .setParseErrors(mParseErrors)
- .setZeroLifetimeRas(mZeroLifetimeRas)
- .setProgramUpdates(mProgramUpdates)
- .setDurationMs(nowMs - mStart)
- .setMaxProgramSize(mApfCapabilities.maximumApfProgramSize)
- .setProgramUpdatesAll(mNumProgramUpdates)
- .setProgramUpdatesAllowingMulticast(mNumProgramUpdatesAllowingMulticast)
- .build();
- mMetricsLog.log(stats);
- logApfProgramEventLocked(nowMs / DateUtils.SECOND_IN_MILLIS);
- }
- }
- }
-
- private static final String TAG = "ApfFilter";
- private static final boolean DBG = true;
- private static final boolean VDBG = false;
-
- private static final int ETH_HEADER_LEN = 14;
- private static final int ETH_DEST_ADDR_OFFSET = 0;
- private static final int ETH_ETHERTYPE_OFFSET = 12;
- private static final int ETH_TYPE_MIN = 0x0600;
- private static final int ETH_TYPE_MAX = 0xFFFF;
- private static final byte[] ETH_BROADCAST_MAC_ADDRESS =
- {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
- // TODO: Make these offsets relative to end of link-layer header; don't include ETH_HEADER_LEN.
- private static final int IPV4_TOTAL_LENGTH_OFFSET = ETH_HEADER_LEN + 2;
- private static final int IPV4_FRAGMENT_OFFSET_OFFSET = ETH_HEADER_LEN + 6;
- // Endianness is not an issue for this constant because the APF interpreter always operates in
- // network byte order.
- private static final int IPV4_FRAGMENT_OFFSET_MASK = 0x1fff;
- private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
- private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
- private static final int IPV4_ANY_HOST_ADDRESS = 0;
- private static final int IPV4_BROADCAST_ADDRESS = -1; // 255.255.255.255
- private static final int IPV4_HEADER_LEN = 20; // Without options
-
- // Traffic class and Flow label are not byte aligned. Luckily we
- // don't care about either value so we'll consider bytes 1-3 of the
- // IPv6 header as don't care.
- private static final int IPV6_FLOW_LABEL_OFFSET = ETH_HEADER_LEN + 1;
- private static final int IPV6_FLOW_LABEL_LEN = 3;
- private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
- private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
- private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
- private static final int IPV6_HEADER_LEN = 40;
- // The IPv6 all nodes address ff02::1
- private static final byte[] IPV6_ALL_NODES_ADDRESS =
- { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
-
- private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
-
- // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
- private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2;
- private static final int UDP_HEADER_LEN = 8;
-
- private static final int TCP_HEADER_SIZE_OFFSET = 12;
-
- private static final int DHCP_CLIENT_PORT = 68;
- // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
- private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28;
-
- private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
- private static final byte[] ARP_IPV4_HEADER = {
- 0, 1, // Hardware type: Ethernet (1)
- 8, 0, // Protocol type: IP (0x0800)
- 6, // Hardware size: 6
- 4, // Protocol size: 4
- };
- private static final int ARP_OPCODE_OFFSET = ARP_HEADER_OFFSET + 6;
- // Opcode: ARP request (0x0001), ARP reply (0x0002)
- private static final short ARP_OPCODE_REQUEST = 1;
- private static final short ARP_OPCODE_REPLY = 2;
- private static final int ARP_SOURCE_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 14;
- private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 24;
- // Do not log ApfProgramEvents whose actual lifetimes was less than this.
- private static final int APF_PROGRAM_EVENT_LIFETIME_THRESHOLD = 2;
- // Limit on the Black List size to cap on program usage for this
- // TODO: Select a proper max length
- private static final int APF_MAX_ETH_TYPE_BLACK_LIST_LEN = 20;
-
- private final ApfCapabilities mApfCapabilities;
- private final IpClientCallbacksWrapper mIpClientCallback;
- private final InterfaceParams mInterfaceParams;
- private final IpConnectivityLog mMetricsLog;
-
- @VisibleForTesting
- byte[] mHardwareAddress;
- @VisibleForTesting
- ReceiveThread mReceiveThread;
- @GuardedBy("this")
- private long mUniqueCounter;
- @GuardedBy("this")
- private boolean mMulticastFilter;
- @GuardedBy("this")
- private boolean mInDozeMode;
- private final boolean mDrop802_3Frames;
- private final int[] mEthTypeBlackList;
-
- // Detects doze mode state transitions.
- private final BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) {
- PowerManager powerManager =
- (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- final boolean deviceIdle = powerManager.isDeviceIdleMode();
- setDozeMode(deviceIdle);
- }
- }
- };
- private final Context mContext;
-
- // Our IPv4 address, if we have just one, otherwise null.
- @GuardedBy("this")
- private byte[] mIPv4Address;
- // The subnet prefix length of our IPv4 network. Only valid if mIPv4Address is not null.
- @GuardedBy("this")
- private int mIPv4PrefixLength;
-
- @VisibleForTesting
- ApfFilter(Context context, ApfConfiguration config, InterfaceParams ifParams,
- IpClientCallbacksWrapper ipClientCallback, IpConnectivityLog log) {
- mApfCapabilities = config.apfCapabilities;
- mIpClientCallback = ipClientCallback;
- mInterfaceParams = ifParams;
- mMulticastFilter = config.multicastFilter;
- mDrop802_3Frames = config.ieee802_3Filter;
- mContext = context;
-
- if (mApfCapabilities.hasDataAccess()) {
- mCountAndPassLabel = "countAndPass";
- mCountAndDropLabel = "countAndDrop";
- } else {
- // APFv4 unsupported: turn jumps to the counter trampolines to immediately PASS or DROP,
- // preserving the original pre-APFv4 behavior.
- mCountAndPassLabel = ApfGenerator.PASS_LABEL;
- mCountAndDropLabel = ApfGenerator.DROP_LABEL;
- }
-
- // Now fill the black list from the passed array
- mEthTypeBlackList = filterEthTypeBlackList(config.ethTypeBlackList);
-
- mMetricsLog = log;
-
- // TODO: ApfFilter should not generate programs until IpClient sends provisioning success.
- maybeStartFilter();
-
- // Listen for doze-mode transition changes to enable/disable the IPv6 multicast filter.
- mContext.registerReceiver(mDeviceIdleReceiver,
- new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED));
- }
-
- public synchronized void setDataSnapshot(byte[] data) {
- mDataSnapshot = data;
- }
-
- private void log(String s) {
- Log.d(TAG, "(" + mInterfaceParams.name + "): " + s);
- }
-
- @GuardedBy("this")
- private long getUniqueNumberLocked() {
- return mUniqueCounter++;
- }
-
- @GuardedBy("this")
- private static int[] filterEthTypeBlackList(int[] ethTypeBlackList) {
- ArrayList<Integer> bl = new ArrayList<Integer>();
-
- for (int p : ethTypeBlackList) {
- // Check if the protocol is a valid ether type
- if ((p < ETH_TYPE_MIN) || (p > ETH_TYPE_MAX)) {
- continue;
- }
-
- // Check if the protocol is not repeated in the passed array
- if (bl.contains(p)) {
- continue;
- }
-
- // Check if list reach its max size
- if (bl.size() == APF_MAX_ETH_TYPE_BLACK_LIST_LEN) {
- Log.w(TAG, "Passed EthType Black List size too large (" + bl.size() +
- ") using top " + APF_MAX_ETH_TYPE_BLACK_LIST_LEN + " protocols");
- break;
- }
-
- // Now add the protocol to the list
- bl.add(p);
- }
-
- return bl.stream().mapToInt(Integer::intValue).toArray();
- }
-
- /**
- * Attempt to start listening for RAs and, if RAs are received, generating and installing
- * filters to ignore useless RAs.
- */
- @VisibleForTesting
- void maybeStartFilter() {
- FileDescriptor socket;
- try {
- mHardwareAddress = mInterfaceParams.macAddr.toByteArray();
- synchronized(this) {
- // Clear the APF memory to reset all counters upon connecting to the first AP
- // in an SSID. This is limited to APFv4 devices because this large write triggers
- // a crash on some older devices (b/78905546).
- if (mApfCapabilities.hasDataAccess()) {
- byte[] zeroes = new byte[mApfCapabilities.maximumApfProgramSize];
- mIpClientCallback.installPacketFilter(zeroes);
- }
-
- // Install basic filters
- installNewProgramLocked();
- }
- socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6);
- SocketAddress addr = makePacketSocketAddress(
- (short) ETH_P_IPV6, mInterfaceParams.index);
- Os.bind(socket, addr);
- NetworkStackUtils.attachRaFilter(socket, mApfCapabilities.apfPacketFormat);
- } catch(SocketException|ErrnoException e) {
- Log.e(TAG, "Error starting filter", e);
- return;
- }
- mReceiveThread = new ReceiveThread(socket);
- mReceiveThread.start();
- }
-
- // Returns seconds since device boot.
- @VisibleForTesting
- protected long currentTimeSeconds() {
- return SystemClock.elapsedRealtime() / DateUtils.SECOND_IN_MILLIS;
- }
-
- public static class InvalidRaException extends Exception {
- public InvalidRaException(String m) {
- super(m);
- }
- }
-
- // A class to hold information about an RA.
- @VisibleForTesting
- class Ra {
- // From RFC4861:
- private static final int ICMP6_RA_HEADER_LEN = 16;
- private static final int ICMP6_RA_CHECKSUM_OFFSET =
- ETH_HEADER_LEN + IPV6_HEADER_LEN + 2;
- private static final int ICMP6_RA_CHECKSUM_LEN = 2;
- private static final int ICMP6_RA_OPTION_OFFSET =
- ETH_HEADER_LEN + IPV6_HEADER_LEN + ICMP6_RA_HEADER_LEN;
- private static final int ICMP6_RA_ROUTER_LIFETIME_OFFSET =
- ETH_HEADER_LEN + IPV6_HEADER_LEN + 6;
- private static final int ICMP6_RA_ROUTER_LIFETIME_LEN = 2;
- // Prefix information option.
- private static final int ICMP6_PREFIX_OPTION_TYPE = 3;
- private static final int ICMP6_PREFIX_OPTION_LEN = 32;
- private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4;
- private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN = 4;
- private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET = 8;
- private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN = 4;
-
- // From RFC6106: Recursive DNS Server option
- private static final int ICMP6_RDNSS_OPTION_TYPE = 25;
- // From RFC6106: DNS Search List option
- private static final int ICMP6_DNSSL_OPTION_TYPE = 31;
-
- // From RFC4191: Route Information option
- private static final int ICMP6_ROUTE_INFO_OPTION_TYPE = 24;
- // Above three options all have the same format:
- private static final int ICMP6_4_BYTE_LIFETIME_OFFSET = 4;
- private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4;
-
- // Note: mPacket's position() cannot be assumed to be reset.
- private final ByteBuffer mPacket;
- // List of binary ranges that include the whole packet except the lifetimes.
- // Pairs consist of offset and length.
- private final ArrayList<Pair<Integer, Integer>> mNonLifetimes =
- new ArrayList<Pair<Integer, Integer>>();
- // Minimum lifetime in packet
- long mMinLifetime;
- // When the packet was last captured, in seconds since Unix Epoch
- long mLastSeen;
-
- // For debugging only. Offsets into the packet where PIOs are.
- private final ArrayList<Integer> mPrefixOptionOffsets = new ArrayList<>();
-
- // For debugging only. Offsets into the packet where RDNSS options are.
- private final ArrayList<Integer> mRdnssOptionOffsets = new ArrayList<>();
-
- // For debugging only. How many times this RA was seen.
- int seenCount = 0;
-
- // For debugging only. Returns the hex representation of the last matching packet.
- String getLastMatchingPacket() {
- return HexDump.toHexString(mPacket.array(), 0, mPacket.capacity(),
- false /* lowercase */);
- }
-
- // For debugging only. Returns the string representation of the IPv6 address starting at
- // position pos in the packet.
- private String IPv6AddresstoString(int pos) {
- try {
- byte[] array = mPacket.array();
- // Can't just call copyOfRange() and see if it throws, because if it reads past the
- // end it pads with zeros instead of throwing.
- if (pos < 0 || pos + 16 > array.length || pos + 16 < pos) {
- return "???";
- }
- byte[] addressBytes = Arrays.copyOfRange(array, pos, pos + 16);
- InetAddress address = (Inet6Address) InetAddress.getByAddress(addressBytes);
- return address.getHostAddress();
- } catch (UnsupportedOperationException e) {
- // array() failed. Cannot happen, mPacket is array-backed and read-write.
- return "???";
- } catch (ClassCastException|UnknownHostException e) {
- // Cannot happen.
- return "???";
- }
- }
-
- // Can't be static because it's in a non-static inner class.
- // TODO: Make this static once RA is its own class.
- private void prefixOptionToString(StringBuffer sb, int offset) {
- String prefix = IPv6AddresstoString(offset + 16);
- int length = getUint8(mPacket, offset + 2);
- long valid = getUint32(mPacket, offset + 4);
- long preferred = getUint32(mPacket, offset + 8);
- sb.append(String.format("%s/%d %ds/%ds ", prefix, length, valid, preferred));
- }
-
- private void rdnssOptionToString(StringBuffer sb, int offset) {
- int optLen = getUint8(mPacket, offset + 1) * 8;
- if (optLen < 24) return; // Malformed or empty.
- long lifetime = getUint32(mPacket, offset + 4);
- int numServers = (optLen - 8) / 16;
- sb.append("DNS ").append(lifetime).append("s");
- for (int server = 0; server < numServers; server++) {
- sb.append(" ").append(IPv6AddresstoString(offset + 8 + 16 * server));
- }
- }
-
- public String toString() {
- try {
- StringBuffer sb = new StringBuffer();
- sb.append(String.format("RA %s -> %s %ds ",
- IPv6AddresstoString(IPV6_SRC_ADDR_OFFSET),
- IPv6AddresstoString(IPV6_DEST_ADDR_OFFSET),
- getUint16(mPacket, ICMP6_RA_ROUTER_LIFETIME_OFFSET)));
- for (int i: mPrefixOptionOffsets) {
- prefixOptionToString(sb, i);
- }
- for (int i: mRdnssOptionOffsets) {
- rdnssOptionToString(sb, i);
- }
- return sb.toString();
- } catch (BufferUnderflowException|IndexOutOfBoundsException e) {
- return "<Malformed RA>";
- }
- }
-
- /**
- * Add a binary range of the packet that does not include a lifetime to mNonLifetimes.
- * Assumes mPacket.position() is as far as we've parsed the packet.
- * @param lastNonLifetimeStart offset within packet of where the last binary range of
- * data not including a lifetime.
- * @param lifetimeOffset offset from mPacket.position() to the next lifetime data.
- * @param lifetimeLength length of the next lifetime data.
- * @return offset within packet of where the next binary range of data not including
- * a lifetime. This can be passed into the next invocation of this function
- * via {@code lastNonLifetimeStart}.
- */
- private int addNonLifetime(int lastNonLifetimeStart, int lifetimeOffset,
- int lifetimeLength) {
- lifetimeOffset += mPacket.position();
- mNonLifetimes.add(new Pair<Integer, Integer>(lastNonLifetimeStart,
- lifetimeOffset - lastNonLifetimeStart));
- return lifetimeOffset + lifetimeLength;
- }
-
- private int addNonLifetimeU32(int lastNonLifetimeStart) {
- return addNonLifetime(lastNonLifetimeStart,
- ICMP6_4_BYTE_LIFETIME_OFFSET, ICMP6_4_BYTE_LIFETIME_LEN);
- }
-
- // Note that this parses RA and may throw InvalidRaException (from
- // Buffer.position(int) or due to an invalid-length option) or IndexOutOfBoundsException
- // (from ByteBuffer.get(int) ) if parsing encounters something non-compliant with
- // specifications.
- Ra(byte[] packet, int length) throws InvalidRaException {
- if (length < ICMP6_RA_OPTION_OFFSET) {
- throw new InvalidRaException("Not an ICMP6 router advertisement");
- }
-
- mPacket = ByteBuffer.wrap(Arrays.copyOf(packet, length));
- mLastSeen = currentTimeSeconds();
-
- // Sanity check packet in case a packet arrives before we attach RA filter
- // to our packet socket. b/29586253
- if (getUint16(mPacket, ETH_ETHERTYPE_OFFSET) != ETH_P_IPV6 ||
- getUint8(mPacket, IPV6_NEXT_HEADER_OFFSET) != IPPROTO_ICMPV6 ||
- getUint8(mPacket, ICMP6_TYPE_OFFSET) != ICMPV6_ROUTER_ADVERTISEMENT) {
- throw new InvalidRaException("Not an ICMP6 router advertisement");
- }
-
-
- RaEvent.Builder builder = new RaEvent.Builder();
-
- // Ignore the flow label and low 4 bits of traffic class.
- int lastNonLifetimeStart = addNonLifetime(0,
- IPV6_FLOW_LABEL_OFFSET,
- IPV6_FLOW_LABEL_LEN);
-
- // Ignore the checksum.
- lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
- ICMP6_RA_CHECKSUM_OFFSET,
- ICMP6_RA_CHECKSUM_LEN);
-
- // Parse router lifetime
- lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
- ICMP6_RA_ROUTER_LIFETIME_OFFSET,
- ICMP6_RA_ROUTER_LIFETIME_LEN);
- builder.updateRouterLifetime(getUint16(mPacket, ICMP6_RA_ROUTER_LIFETIME_OFFSET));
-
- // Ensures that the RA is not truncated.
- mPacket.position(ICMP6_RA_OPTION_OFFSET);
- while (mPacket.hasRemaining()) {
- final int position = mPacket.position();
- final int optionType = getUint8(mPacket, position);
- final int optionLength = getUint8(mPacket, position + 1) * 8;
- long lifetime;
- switch (optionType) {
- case ICMP6_PREFIX_OPTION_TYPE:
- // Parse valid lifetime
- lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
- ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET,
- ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN);
- lifetime = getUint32(mPacket,
- position + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET);
- builder.updatePrefixValidLifetime(lifetime);
- // Parse preferred lifetime
- lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart,
- ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET,
- ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN);
- lifetime = getUint32(mPacket,
- position + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET);
- builder.updatePrefixPreferredLifetime(lifetime);
- mPrefixOptionOffsets.add(position);
- break;
- // These three options have the same lifetime offset and size, and
- // are processed with the same specialized addNonLifetimeU32:
- case ICMP6_RDNSS_OPTION_TYPE:
- mRdnssOptionOffsets.add(position);
- lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
- lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
- builder.updateRdnssLifetime(lifetime);
- break;
- case ICMP6_ROUTE_INFO_OPTION_TYPE:
- lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
- lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
- builder.updateRouteInfoLifetime(lifetime);
- break;
- case ICMP6_DNSSL_OPTION_TYPE:
- lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart);
- lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET);
- builder.updateDnsslLifetime(lifetime);
- break;
- default:
- // RFC4861 section 4.2 dictates we ignore unknown options for fowards
- // compatibility.
- break;
- }
- if (optionLength <= 0) {
- throw new InvalidRaException(String.format(
- "Invalid option length opt=%d len=%d", optionType, optionLength));
- }
- mPacket.position(position + optionLength);
- }
- // Mark non-lifetime bytes since last lifetime.
- addNonLifetime(lastNonLifetimeStart, 0, 0);
- mMinLifetime = minLifetime(packet, length);
- mMetricsLog.log(builder.build());
- }
-
- // Ignoring lifetimes (which may change) does {@code packet} match this RA?
- boolean matches(byte[] packet, int length) {
- if (length != mPacket.capacity()) return false;
- byte[] referencePacket = mPacket.array();
- for (Pair<Integer, Integer> nonLifetime : mNonLifetimes) {
- for (int i = nonLifetime.first; i < (nonLifetime.first + nonLifetime.second); i++) {
- if (packet[i] != referencePacket[i]) return false;
- }
- }
- return true;
- }
-
- // What is the minimum of all lifetimes within {@code packet} in seconds?
- // Precondition: matches(packet, length) already returned true.
- long minLifetime(byte[] packet, int length) {
- long minLifetime = Long.MAX_VALUE;
- // Wrap packet in ByteBuffer so we can read big-endian values easily
- ByteBuffer byteBuffer = ByteBuffer.wrap(packet);
- for (int i = 0; (i + 1) < mNonLifetimes.size(); i++) {
- int offset = mNonLifetimes.get(i).first + mNonLifetimes.get(i).second;
-
- // The flow label is in mNonLifetimes, but it's not a lifetime.
- if (offset == IPV6_FLOW_LABEL_OFFSET) {
- continue;
- }
-
- // The checksum is in mNonLifetimes, but it's not a lifetime.
- if (offset == ICMP6_RA_CHECKSUM_OFFSET) {
- continue;
- }
-
- final int lifetimeLength = mNonLifetimes.get(i+1).first - offset;
- final long optionLifetime;
- switch (lifetimeLength) {
- case 2:
- optionLifetime = getUint16(byteBuffer, offset);
- break;
- case 4:
- optionLifetime = getUint32(byteBuffer, offset);
- break;
- default:
- throw new IllegalStateException("bogus lifetime size " + lifetimeLength);
- }
- minLifetime = Math.min(minLifetime, optionLifetime);
- }
- return minLifetime;
- }
-
- // How many seconds does this RA's have to live, taking into account the fact
- // that we might have seen it a while ago.
- long currentLifetime() {
- return mMinLifetime - (currentTimeSeconds() - mLastSeen);
- }
-
- boolean isExpired() {
- // TODO: We may want to handle 0 lifetime RAs differently, if they are common. We'll
- // have to calculate the filter lifetime specially as a fraction of 0 is still 0.
- return currentLifetime() <= 0;
- }
-
- // Append a filter for this RA to {@code gen}. Jump to DROP_LABEL if it should be dropped.
- // Jump to the next filter if packet doesn't match this RA.
- @GuardedBy("ApfFilter.this")
- long generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- String nextFilterLabel = "Ra" + getUniqueNumberLocked();
- // Skip if packet is not the right size
- gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT);
- gen.addJumpIfR0NotEquals(mPacket.capacity(), nextFilterLabel);
- int filterLifetime = (int)(currentLifetime() / FRACTION_OF_LIFETIME_TO_FILTER);
- // Skip filter if expired
- gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT);
- gen.addJumpIfR0GreaterThan(filterLifetime, nextFilterLabel);
- for (int i = 0; i < mNonLifetimes.size(); i++) {
- // Generate code to match the packet bytes
- Pair<Integer, Integer> nonLifetime = mNonLifetimes.get(i);
- // Don't generate JNEBS instruction for 0 bytes as it always fails the
- // ASSERT_FORWARD_IN_PROGRAM(pc + cmp_imm - 1) check where cmp_imm is
- // the number of bytes to compare. nonLifetime is zero between the
- // valid and preferred lifetimes in the prefix option.
- if (nonLifetime.second != 0) {
- gen.addLoadImmediate(Register.R0, nonLifetime.first);
- gen.addJumpIfBytesNotEqual(Register.R0,
- Arrays.copyOfRange(mPacket.array(), nonLifetime.first,
- nonLifetime.first + nonLifetime.second),
- nextFilterLabel);
- }
- // Generate code to test the lifetimes haven't gone down too far
- if ((i + 1) < mNonLifetimes.size()) {
- Pair<Integer, Integer> nextNonLifetime = mNonLifetimes.get(i + 1);
- int offset = nonLifetime.first + nonLifetime.second;
-
- // Skip the Flow label.
- if (offset == IPV6_FLOW_LABEL_OFFSET) {
- continue;
- }
- // Skip the checksum.
- if (offset == ICMP6_RA_CHECKSUM_OFFSET) {
- continue;
- }
- int length = nextNonLifetime.first - offset;
- switch (length) {
- case 4: gen.addLoad32(Register.R0, offset); break;
- case 2: gen.addLoad16(Register.R0, offset); break;
- default: throw new IllegalStateException("bogus lifetime size " + length);
- }
- gen.addJumpIfR0LessThan(filterLifetime, nextFilterLabel);
- }
- }
- maybeSetupCounter(gen, Counter.DROPPED_RA);
- gen.addJump(mCountAndDropLabel);
- gen.defineLabel(nextFilterLabel);
- return filterLifetime;
- }
- }
-
- // TODO: Refactor these subclasses to avoid so much repetition.
- private abstract static class KeepalivePacket {
- // Note that the offset starts from IP header.
- // These must be added ether header length when generating program.
- static final int IP_HEADER_OFFSET = 0;
- static final int IPV4_SRC_ADDR_OFFSET = IP_HEADER_OFFSET + 12;
-
- // Append a filter for this keepalive ack to {@code gen}.
- // Jump to drop if it matches the keepalive ack.
- // Jump to the next filter if packet doesn't match the keepalive ack.
- abstract void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException;
- }
-
- // A class to hold NAT-T keepalive ack information.
- private class NattKeepaliveResponse extends KeepalivePacket {
- static final int UDP_LENGTH_OFFSET = 4;
- static final int UDP_HEADER_LEN = 8;
-
- protected class NattKeepaliveResponseData {
- public final byte[] srcAddress;
- public final int srcPort;
- public final byte[] dstAddress;
- public final int dstPort;
-
- NattKeepaliveResponseData(final NattKeepalivePacketDataParcelable sentKeepalivePacket) {
- srcAddress = sentKeepalivePacket.dstAddress;
- srcPort = sentKeepalivePacket.dstPort;
- dstAddress = sentKeepalivePacket.srcAddress;
- dstPort = sentKeepalivePacket.srcPort;
- }
- }
-
- protected final NattKeepaliveResponseData mPacket;
- protected final byte[] mSrcDstAddr;
- protected final byte[] mPortFingerprint;
- // NAT-T keepalive packet
- protected final byte[] mPayload = {(byte) 0xff};
-
- NattKeepaliveResponse(final NattKeepalivePacketDataParcelable sentKeepalivePacket) {
- mPacket = new NattKeepaliveResponseData(sentKeepalivePacket);
- mSrcDstAddr = concatArrays(mPacket.srcAddress, mPacket.dstAddress);
- mPortFingerprint = generatePortFingerprint(mPacket.srcPort, mPacket.dstPort);
- }
-
- byte[] generatePortFingerprint(int srcPort, int dstPort) {
- final ByteBuffer fp = ByteBuffer.allocate(4);
- fp.order(ByteOrder.BIG_ENDIAN);
- fp.putShort((short) srcPort);
- fp.putShort((short) dstPort);
- return fp.array();
- }
-
- @Override
- void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- final String nextFilterLabel = "natt_keepalive_filter" + getUniqueNumberLocked();
-
- gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET);
- gen.addJumpIfBytesNotEqual(Register.R0, mSrcDstAddr, nextFilterLabel);
-
- // A NAT-T keepalive packet contains 1 byte payload with the value 0xff
- // Check payload length is 1
- gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
- gen.addAdd(UDP_HEADER_LEN);
- gen.addSwap();
- gen.addLoad16(Register.R0, IPV4_TOTAL_LENGTH_OFFSET);
- gen.addNeg(Register.R1);
- gen.addAddR1();
- gen.addJumpIfR0NotEquals(1, nextFilterLabel);
-
- // Check that the ports match
- gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
- gen.addAdd(ETH_HEADER_LEN);
- gen.addJumpIfBytesNotEqual(Register.R0, mPortFingerprint, nextFilterLabel);
-
- // Payload offset = R0 + UDP header length
- gen.addAdd(UDP_HEADER_LEN);
- gen.addJumpIfBytesNotEqual(Register.R0, mPayload, nextFilterLabel);
-
- maybeSetupCounter(gen, Counter.DROPPED_IPV4_NATT_KEEPALIVE);
- gen.addJump(mCountAndDropLabel);
- gen.defineLabel(nextFilterLabel);
- }
-
- public String toString() {
- try {
- return String.format("%s -> %s",
- NetworkStackUtils.addressAndPortToString(
- InetAddress.getByAddress(mPacket.srcAddress), mPacket.srcPort),
- NetworkStackUtils.addressAndPortToString(
- InetAddress.getByAddress(mPacket.dstAddress), mPacket.dstPort));
- } catch (UnknownHostException e) {
- return "Unknown host";
- }
- }
- }
-
- // A class to hold TCP keepalive ack information.
- private abstract static class TcpKeepaliveAck extends KeepalivePacket {
- protected static class TcpKeepaliveAckData {
- public final byte[] srcAddress;
- public final int srcPort;
- public final byte[] dstAddress;
- public final int dstPort;
- public final int seq;
- public final int ack;
-
- // Create the characteristics of the ack packet from the sent keepalive packet.
- TcpKeepaliveAckData(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
- srcAddress = sentKeepalivePacket.dstAddress;
- srcPort = sentKeepalivePacket.dstPort;
- dstAddress = sentKeepalivePacket.srcAddress;
- dstPort = sentKeepalivePacket.srcPort;
- seq = sentKeepalivePacket.ack;
- ack = sentKeepalivePacket.seq + 1;
- }
- }
-
- protected final TcpKeepaliveAckData mPacket;
- protected final byte[] mSrcDstAddr;
- protected final byte[] mPortSeqAckFingerprint;
-
- TcpKeepaliveAck(final TcpKeepaliveAckData packet, final byte[] srcDstAddr) {
- mPacket = packet;
- mSrcDstAddr = srcDstAddr;
- mPortSeqAckFingerprint = generatePortSeqAckFingerprint(mPacket.srcPort,
- mPacket.dstPort, mPacket.seq, mPacket.ack);
- }
-
- static byte[] generatePortSeqAckFingerprint(int srcPort, int dstPort, int seq, int ack) {
- final ByteBuffer fp = ByteBuffer.allocate(12);
- fp.order(ByteOrder.BIG_ENDIAN);
- fp.putShort((short) srcPort);
- fp.putShort((short) dstPort);
- fp.putInt(seq);
- fp.putInt(ack);
- return fp.array();
- }
-
- public String toString() {
- try {
- return String.format("%s -> %s , seq=%d, ack=%d",
- NetworkStackUtils.addressAndPortToString(
- InetAddress.getByAddress(mPacket.srcAddress), mPacket.srcPort),
- NetworkStackUtils.addressAndPortToString(
- InetAddress.getByAddress(mPacket.dstAddress), mPacket.dstPort),
- Integer.toUnsignedLong(mPacket.seq),
- Integer.toUnsignedLong(mPacket.ack));
- } catch (UnknownHostException e) {
- return "Unknown host";
- }
- }
-
- // Append a filter for this keepalive ack to {@code gen}.
- // Jump to drop if it matches the keepalive ack.
- // Jump to the next filter if packet doesn't match the keepalive ack.
- abstract void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException;
- }
-
- private class TcpKeepaliveAckV4 extends TcpKeepaliveAck {
-
- TcpKeepaliveAckV4(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
- this(new TcpKeepaliveAckData(sentKeepalivePacket));
- }
- TcpKeepaliveAckV4(final TcpKeepaliveAckData packet) {
- super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */);
- }
-
- @Override
- void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- final String nextFilterLabel = "keepalive_ack" + getUniqueNumberLocked();
-
- gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET);
- gen.addJumpIfBytesNotEqual(Register.R0, mSrcDstAddr, nextFilterLabel);
-
- // Skip to the next filter if it's not zero-sized :
- // TCP_HEADER_SIZE + IPV4_HEADER_SIZE - ipv4_total_length == 0
- // Load the IP header size into R1
- gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
- // Load the TCP header size into R0 (it's indexed by R1)
- gen.addLoad8Indexed(Register.R0, ETH_HEADER_LEN + TCP_HEADER_SIZE_OFFSET);
- // Size offset is in the top nibble, but it must be multiplied by 4, and the two
- // top bits of the low nibble are guaranteed to be zeroes. Right-shift R0 by 2.
- gen.addRightShift(2);
- // R0 += R1 -> R0 contains TCP + IP headers length
- gen.addAddR1();
- // Load IPv4 total length
- gen.addLoad16(Register.R1, IPV4_TOTAL_LENGTH_OFFSET);
- gen.addNeg(Register.R0);
- gen.addAddR1();
- gen.addJumpIfR0NotEquals(0, nextFilterLabel);
- // Add IPv4 header length
- gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
- gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN);
- gen.addAddR1();
- gen.addJumpIfBytesNotEqual(Register.R0, mPortSeqAckFingerprint, nextFilterLabel);
-
- maybeSetupCounter(gen, Counter.DROPPED_IPV4_KEEPALIVE_ACK);
- gen.addJump(mCountAndDropLabel);
- gen.defineLabel(nextFilterLabel);
- }
- }
-
- private class TcpKeepaliveAckV6 extends TcpKeepaliveAck {
- TcpKeepaliveAckV6(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
- this(new TcpKeepaliveAckData(sentKeepalivePacket));
- }
- TcpKeepaliveAckV6(final TcpKeepaliveAckData packet) {
- super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */);
- }
-
- @Override
- void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- throw new UnsupportedOperationException("IPv6 TCP Keepalive is not supported yet");
- }
- }
-
- // Maximum number of RAs to filter for.
- private static final int MAX_RAS = 10;
-
- @GuardedBy("this")
- private ArrayList<Ra> mRas = new ArrayList<>();
- @GuardedBy("this")
- private SparseArray<KeepalivePacket> mKeepalivePackets = new SparseArray<>();
-
- // There is always some marginal benefit to updating the installed APF program when an RA is
- // seen because we can extend the program's lifetime slightly, but there is some cost to
- // updating the program, so don't bother unless the program is going to expire soon. This
- // constant defines "soon" in seconds.
- private static final long MAX_PROGRAM_LIFETIME_WORTH_REFRESHING = 30;
- // We don't want to filter an RA for it's whole lifetime as it'll be expired by the time we ever
- // see a refresh. Using half the lifetime might be a good idea except for the fact that
- // packets may be dropped, so let's use 6.
- private static final int FRACTION_OF_LIFETIME_TO_FILTER = 6;
-
- // When did we last install a filter program? In seconds since Unix Epoch.
- @GuardedBy("this")
- private long mLastTimeInstalledProgram;
- // How long should the last installed filter program live for? In seconds.
- @GuardedBy("this")
- private long mLastInstalledProgramMinLifetime;
- @GuardedBy("this")
- private ApfProgramEvent.Builder mLastInstallEvent;
-
- // For debugging only. The last program installed.
- @GuardedBy("this")
- private byte[] mLastInstalledProgram;
-
- /**
- * For debugging only. Contains the latest APF buffer snapshot captured from the firmware.
- *
- * A typical size for this buffer is 4KB. It is present only if the WiFi HAL supports
- * IWifiStaIface#readApfPacketFilterData(), and the APF interpreter advertised support for
- * the opcodes to access the data buffer (LDDW and STDW).
- */
- @GuardedBy("this") @Nullable
- private byte[] mDataSnapshot;
-
- // How many times the program was updated since we started.
- @GuardedBy("this")
- private int mNumProgramUpdates = 0;
- // How many times the program was updated since we started for allowing multicast traffic.
- @GuardedBy("this")
- private int mNumProgramUpdatesAllowingMulticast = 0;
-
- /**
- * Generate filter code to process ARP packets. Execution of this code ends in either the
- * DROP_LABEL or PASS_LABEL and does not fall off the end.
- * Preconditions:
- * - Packet being filtered is ARP
- */
- @GuardedBy("this")
- private void generateArpFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- // Here's a basic summary of what the ARP filter program does:
- //
- // if not ARP IPv4
- // pass
- // if not ARP IPv4 reply or request
- // pass
- // if ARP reply source ip is 0.0.0.0
- // drop
- // if unicast ARP reply
- // pass
- // if interface has no IPv4 address
- // if target ip is 0.0.0.0
- // drop
- // else
- // if target ip is not the interface ip
- // drop
- // pass
-
- final String checkTargetIPv4 = "checkTargetIPv4";
-
- // Pass if not ARP IPv4.
- gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET);
- maybeSetupCounter(gen, Counter.PASSED_ARP_NON_IPV4);
- gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, mCountAndPassLabel);
-
- // Pass if unknown ARP opcode.
- gen.addLoad16(Register.R0, ARP_OPCODE_OFFSET);
- gen.addJumpIfR0Equals(ARP_OPCODE_REQUEST, checkTargetIPv4); // Skip to unicast check
- maybeSetupCounter(gen, Counter.PASSED_ARP_UNKNOWN);
- gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, mCountAndPassLabel);
-
- // Drop if ARP reply source IP is 0.0.0.0
- gen.addLoad32(Register.R0, ARP_SOURCE_IP_ADDRESS_OFFSET);
- maybeSetupCounter(gen, Counter.DROPPED_ARP_REPLY_SPA_NO_HOST);
- gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel);
-
- // Pass if unicast reply.
- gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
- maybeSetupCounter(gen, Counter.PASSED_ARP_UNICAST_REPLY);
- gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
-
- // Either a unicast request, a unicast reply, or a broadcast reply.
- gen.defineLabel(checkTargetIPv4);
- if (mIPv4Address == null) {
- // When there is no IPv4 address, drop GARP replies (b/29404209).
- gen.addLoad32(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
- maybeSetupCounter(gen, Counter.DROPPED_GARP_REPLY);
- gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel);
- } else {
- // When there is an IPv4 address, drop unicast/broadcast requests
- // and broadcast replies with a different target IPv4 address.
- gen.addLoadImmediate(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
- maybeSetupCounter(gen, Counter.DROPPED_ARP_OTHER_HOST);
- gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, mCountAndDropLabel);
- }
-
- maybeSetupCounter(gen, Counter.PASSED_ARP);
- gen.addJump(mCountAndPassLabel);
- }
-
- /**
- * Generate filter code to process IPv4 packets. Execution of this code ends in either the
- * DROP_LABEL or PASS_LABEL and does not fall off the end.
- * Preconditions:
- * - Packet being filtered is IPv4
- */
- @GuardedBy("this")
- private void generateIPv4FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- // Here's a basic summary of what the IPv4 filter program does:
- //
- // if filtering multicast (i.e. multicast lock not held):
- // if it's DHCP destined to our MAC:
- // pass
- // if it's L2 broadcast:
- // drop
- // if it's IPv4 multicast:
- // drop
- // if it's IPv4 broadcast:
- // drop
- // if keepalive ack
- // drop
- // pass
-
- if (mMulticastFilter) {
- final String skipDhcpv4Filter = "skip_dhcp_v4_filter";
-
- // Pass DHCP addressed to us.
- // Check it's UDP.
- gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
- gen.addJumpIfR0NotEquals(IPPROTO_UDP, skipDhcpv4Filter);
- // Check it's not a fragment. This matches the BPF filter installed by the DHCP client.
- gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET);
- gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, skipDhcpv4Filter);
- // Check it's addressed to DHCP client port.
- gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
- gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET);
- gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, skipDhcpv4Filter);
- // Check it's DHCP to our MAC address.
- gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET);
- // NOTE: Relies on R1 containing IPv4 header offset.
- gen.addAddR1();
- gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, skipDhcpv4Filter);
- maybeSetupCounter(gen, Counter.PASSED_DHCP);
- gen.addJump(mCountAndPassLabel);
-
- // Drop all multicasts/broadcasts.
- gen.defineLabel(skipDhcpv4Filter);
-
- // If IPv4 destination address is in multicast range, drop.
- gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET);
- gen.addAnd(0xf0);
- maybeSetupCounter(gen, Counter.DROPPED_IPV4_MULTICAST);
- gen.addJumpIfR0Equals(0xe0, mCountAndDropLabel);
-
- // If IPv4 broadcast packet, drop regardless of L2 (b/30231088).
- maybeSetupCounter(gen, Counter.DROPPED_IPV4_BROADCAST_ADDR);
- gen.addLoad32(Register.R0, IPV4_DEST_ADDR_OFFSET);
- gen.addJumpIfR0Equals(IPV4_BROADCAST_ADDRESS, mCountAndDropLabel);
- if (mIPv4Address != null && mIPv4PrefixLength < 31) {
- maybeSetupCounter(gen, Counter.DROPPED_IPV4_BROADCAST_NET);
- int broadcastAddr = ipv4BroadcastAddress(mIPv4Address, mIPv4PrefixLength);
- gen.addJumpIfR0Equals(broadcastAddr, mCountAndDropLabel);
- }
-
- // If any TCP keepalive filter matches, drop
- generateV4KeepaliveFilters(gen);
-
- // If any NAT-T keepalive filter matches, drop
- generateV4NattKeepaliveFilters(gen);
-
- // Otherwise, this is an IPv4 unicast, pass
- // If L2 broadcast packet, drop.
- // TODO: can we invert this condition to fall through to the common pass case below?
- maybeSetupCounter(gen, Counter.PASSED_IPV4_UNICAST);
- gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
- gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
- maybeSetupCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST);
- gen.addJump(mCountAndDropLabel);
- } else {
- generateV4KeepaliveFilters(gen);
- generateV4NattKeepaliveFilters(gen);
- }
-
- // Otherwise, pass
- maybeSetupCounter(gen, Counter.PASSED_IPV4);
- gen.addJump(mCountAndPassLabel);
- }
-
- private void generateKeepaliveFilters(ApfGenerator gen, Class<?> filterType, int proto,
- int offset, String label) throws IllegalInstructionException {
- final boolean haveKeepaliveResponses = NetworkStackUtils.any(mKeepalivePackets,
- ack -> filterType.isInstance(ack));
-
- // If no keepalive packets of this type
- if (!haveKeepaliveResponses) return;
-
- // If not the right proto, skip keepalive filters
- gen.addLoad8(Register.R0, offset);
- gen.addJumpIfR0NotEquals(proto, label);
-
- // Drop Keepalive responses
- for (int i = 0; i < mKeepalivePackets.size(); ++i) {
- final KeepalivePacket response = mKeepalivePackets.valueAt(i);
- if (filterType.isInstance(response)) response.generateFilterLocked(gen);
- }
-
- gen.defineLabel(label);
- }
-
- private void generateV4KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException {
- generateKeepaliveFilters(gen, TcpKeepaliveAckV4.class, IPPROTO_TCP, IPV4_PROTOCOL_OFFSET,
- "skip_v4_keepalive_filter");
- }
-
- private void generateV4NattKeepaliveFilters(ApfGenerator gen)
- throws IllegalInstructionException {
- generateKeepaliveFilters(gen, NattKeepaliveResponse.class,
- IPPROTO_UDP, IPV4_PROTOCOL_OFFSET, "skip_v4_nattkeepalive_filter");
- }
-
- /**
- * Generate filter code to process IPv6 packets. Execution of this code ends in either the
- * DROP_LABEL or PASS_LABEL, or falls off the end for ICMPv6 packets.
- * Preconditions:
- * - Packet being filtered is IPv6
- */
- @GuardedBy("this")
- private void generateIPv6FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
- // Here's a basic summary of what the IPv6 filter program does:
- //
- // if we're dropping multicast
- // if it's not IPCMv6 or it's ICMPv6 but we're in doze mode:
- // if it's multicast:
- // drop
- // pass
- // if it's ICMPv6 RS to any:
- // drop
- // if it's ICMPv6 NA to ff02::1:
- // drop
- // if keepalive ack
- // drop
-
- gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET);
-
- // Drop multicast if the multicast filter is enabled.
- if (mMulticastFilter) {
- final String skipIPv6MulticastFilterLabel = "skipIPv6MulticastFilter";
- final String dropAllIPv6MulticastsLabel = "dropAllIPv6Multicast";
-
- // While in doze mode, drop ICMPv6 multicast pings, let the others pass.
- // While awake, let all ICMPv6 multicasts through.
- if (mInDozeMode) {
- // Not ICMPv6? -> Proceed to multicast filtering
- gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, dropAllIPv6MulticastsLabel);
-
- // ICMPv6 but not ECHO? -> Skip the multicast filter.
- // (ICMPv6 ECHO requests will go through the multicast filter below).
- gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
- gen.addJumpIfR0NotEquals(ICMPV6_ECHO_REQUEST_TYPE, skipIPv6MulticastFilterLabel);
- } else {
- gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIPv6MulticastFilterLabel);
- }
-
- // Drop all other packets sent to ff00::/8 (multicast prefix).
- gen.defineLabel(dropAllIPv6MulticastsLabel);
- maybeSetupCounter(gen, Counter.DROPPED_IPV6_NON_ICMP_MULTICAST);
- gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET);
- gen.addJumpIfR0Equals(0xff, mCountAndDropLabel);
- // If any keepalive filter matches, drop
- generateV6KeepaliveFilters(gen);
- // Not multicast. Pass.
- maybeSetupCounter(gen, Counter.PASSED_IPV6_UNICAST_NON_ICMP);
- gen.addJump(mCountAndPassLabel);
- gen.defineLabel(skipIPv6MulticastFilterLabel);
- } else {
- generateV6KeepaliveFilters(gen);
- // If not ICMPv6, pass.
- maybeSetupCounter(gen, Counter.PASSED_IPV6_NON_ICMP);
- gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, mCountAndPassLabel);
- }
-
- // If we got this far, the packet is ICMPv6. Drop some specific types.
-
- // Add unsolicited multicast neighbor announcements filter
- String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA";
- gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
- // Drop all router solicitations (b/32833400)
- maybeSetupCounter(gen, Counter.DROPPED_IPV6_ROUTER_SOLICITATION);
- gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, mCountAndDropLabel);
- // If not neighbor announcements, skip filter.
- gen.addJumpIfR0NotEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, skipUnsolicitedMulticastNALabel);
- // If to ff02::1, drop.
- // TODO: Drop only if they don't contain the address of on-link neighbours.
- gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET);
- gen.addJumpIfBytesNotEqual(Register.R0, IPV6_ALL_NODES_ADDRESS,
- skipUnsolicitedMulticastNALabel);
- maybeSetupCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA);
- gen.addJump(mCountAndDropLabel);
- gen.defineLabel(skipUnsolicitedMulticastNALabel);
- }
-
- private void generateV6KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException {
- generateKeepaliveFilters(gen, TcpKeepaliveAckV6.class, IPPROTO_TCP, IPV6_NEXT_HEADER_OFFSET,
- "skip_v6_keepalive_filter");
- }
-
- /**
- * Begin generating an APF program to:
- * <ul>
- * <li>Drop/Pass 802.3 frames (based on policy)
- * <li>Drop packets with EtherType within the Black List
- * <li>Drop ARP requests not for us, if mIPv4Address is set,
- * <li>Drop IPv4 broadcast packets, except DHCP destined to our MAC,
- * <li>Drop IPv4 multicast packets, if mMulticastFilter,
- * <li>Pass all other IPv4 packets,
- * <li>Drop all broadcast non-IP non-ARP packets.
- * <li>Pass all non-ICMPv6 IPv6 packets,
- * <li>Pass all non-IPv4 and non-IPv6 packets,
- * <li>Drop IPv6 ICMPv6 NAs to ff02::1.
- * <li>Drop IPv6 ICMPv6 RSs.
- * <li>Filter IPv4 packets (see generateIPv4FilterLocked())
- * <li>Filter IPv6 packets (see generateIPv6FilterLocked())
- * <li>Let execution continue off the end of the program for IPv6 ICMPv6 packets. This allows
- * insertion of RA filters here, or if there aren't any, just passes the packets.
- * </ul>
- */
- @GuardedBy("this")
- private ApfGenerator emitPrologueLocked() throws IllegalInstructionException {
- // This is guaranteed to succeed because of the check in maybeCreate.
- ApfGenerator gen = new ApfGenerator(mApfCapabilities.apfVersionSupported);
-
- if (mApfCapabilities.hasDataAccess()) {
- // Increment TOTAL_PACKETS
- maybeSetupCounter(gen, Counter.TOTAL_PACKETS);
- gen.addLoadData(Register.R0, 0); // load counter
- gen.addAdd(1);
- gen.addStoreData(Register.R0, 0); // write-back counter
- }
-
- // Here's a basic summary of what the initial program does:
- //
- // if it's a 802.3 Frame (ethtype < 0x0600):
- // drop or pass based on configurations
- // if it has a ether-type that belongs to the black list
- // drop
- // if it's ARP:
- // insert ARP filter to drop or pass these appropriately
- // if it's IPv4:
- // insert IPv4 filter to drop or pass these appropriately
- // if it's not IPv6:
- // if it's broadcast:
- // drop
- // pass
- // insert IPv6 filter to drop, pass, or fall off the end for ICMPv6 packets
-
- gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET);
-
- if (mDrop802_3Frames) {
- // drop 802.3 frames (ethtype < 0x0600)
- maybeSetupCounter(gen, Counter.DROPPED_802_3_FRAME);
- gen.addJumpIfR0LessThan(ETH_TYPE_MIN, mCountAndDropLabel);
- }
-
- // Handle ether-type black list
- maybeSetupCounter(gen, Counter.DROPPED_ETHERTYPE_BLACKLISTED);
- for (int p : mEthTypeBlackList) {
- gen.addJumpIfR0Equals(p, mCountAndDropLabel);
- }
-
- // Add ARP filters:
- String skipArpFiltersLabel = "skipArpFilters";
- gen.addJumpIfR0NotEquals(ETH_P_ARP, skipArpFiltersLabel);
- generateArpFilterLocked(gen);
- gen.defineLabel(skipArpFiltersLabel);
-
- // Add IPv4 filters:
- String skipIPv4FiltersLabel = "skipIPv4Filters";
- // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not
- // execute the ARP filter, since that filter does not fall through, but either drops or
- // passes.
- gen.addJumpIfR0NotEquals(ETH_P_IP, skipIPv4FiltersLabel);
- generateIPv4FilterLocked(gen);
- gen.defineLabel(skipIPv4FiltersLabel);
-
- // Check for IPv6:
- // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not
- // execute the ARP or IPv4 filters, since those filters do not fall through, but either
- // drop or pass.
- String ipv6FilterLabel = "IPv6Filters";
- gen.addJumpIfR0Equals(ETH_P_IPV6, ipv6FilterLabel);
-
- // Drop non-IP non-ARP broadcasts, pass the rest
- gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
- maybeSetupCounter(gen, Counter.PASSED_NON_IP_UNICAST);
- gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
- maybeSetupCounter(gen, Counter.DROPPED_ETH_BROADCAST);
- gen.addJump(mCountAndDropLabel);
-
- // Add IPv6 filters:
- gen.defineLabel(ipv6FilterLabel);
- generateIPv6FilterLocked(gen);
- return gen;
- }
-
- /**
- * Append packet counting epilogue to the APF program.
- *
- * Currently, the epilogue consists of two trampolines which count passed and dropped packets
- * before jumping to the actual PASS and DROP labels.
- */
- @GuardedBy("this")
- private void emitEpilogue(ApfGenerator gen) throws IllegalInstructionException {
- // If APFv4 is unsupported, no epilogue is necessary: if execution reached this far, it
- // will just fall-through to the PASS label.
- if (!mApfCapabilities.hasDataAccess()) return;
-
- // Execution will reach the bottom of the program if none of the filters match,
- // which will pass the packet to the application processor.
- maybeSetupCounter(gen, Counter.PASSED_IPV6_ICMP);
-
- // Append the count & pass trampoline, which increments the counter at the data address
- // pointed to by R1, then jumps to the pass label. This saves a few bytes over inserting
- // the entire sequence inline for every counter.
- gen.defineLabel(mCountAndPassLabel);
- gen.addLoadData(Register.R0, 0); // R0 = *(R1 + 0)
- gen.addAdd(1); // R0++
- gen.addStoreData(Register.R0, 0); // *(R1 + 0) = R0
- gen.addJump(gen.PASS_LABEL);
-
- // Same as above for the count & drop trampoline.
- gen.defineLabel(mCountAndDropLabel);
- gen.addLoadData(Register.R0, 0); // R0 = *(R1 + 0)
- gen.addAdd(1); // R0++
- gen.addStoreData(Register.R0, 0); // *(R1 + 0) = R0
- gen.addJump(gen.DROP_LABEL);
- }
-
- /**
- * Generate and install a new filter program.
- */
- @GuardedBy("this")
- @VisibleForTesting
- void installNewProgramLocked() {
- purgeExpiredRasLocked();
- ArrayList<Ra> rasToFilter = new ArrayList<>();
- final byte[] program;
- long programMinLifetime = Long.MAX_VALUE;
- long maximumApfProgramSize = mApfCapabilities.maximumApfProgramSize;
- if (mApfCapabilities.hasDataAccess()) {
- // Reserve space for the counters.
- maximumApfProgramSize -= Counter.totalSize();
- }
-
- try {
- // Step 1: Determine how many RA filters we can fit in the program.
- ApfGenerator gen = emitPrologueLocked();
-
- // The epilogue normally goes after the RA filters, but add it early to include its
- // length when estimating the total.
- emitEpilogue(gen);
-
- // Can't fit the program even without any RA filters?
- if (gen.programLengthOverEstimate() > maximumApfProgramSize) {
- Log.e(TAG, "Program exceeds maximum size " + maximumApfProgramSize);
- return;
- }
-
- for (Ra ra : mRas) {
- ra.generateFilterLocked(gen);
- // Stop if we get too big.
- if (gen.programLengthOverEstimate() > maximumApfProgramSize) break;
- rasToFilter.add(ra);
- }
-
- // Step 2: Actually generate the program
- gen = emitPrologueLocked();
- for (Ra ra : rasToFilter) {
- programMinLifetime = Math.min(programMinLifetime, ra.generateFilterLocked(gen));
- }
- emitEpilogue(gen);
- program = gen.generate();
- } catch (IllegalInstructionException|IllegalStateException e) {
- Log.e(TAG, "Failed to generate APF program.", e);
- return;
- }
- final long now = currentTimeSeconds();
- mLastTimeInstalledProgram = now;
- mLastInstalledProgramMinLifetime = programMinLifetime;
- mLastInstalledProgram = program;
- mNumProgramUpdates++;
-
- if (VDBG) {
- hexDump("Installing filter: ", program, program.length);
- }
- mIpClientCallback.installPacketFilter(program);
- logApfProgramEventLocked(now);
- mLastInstallEvent = new ApfProgramEvent.Builder()
- .setLifetime(programMinLifetime)
- .setFilteredRas(rasToFilter.size())
- .setCurrentRas(mRas.size())
- .setProgramLength(program.length)
- .setFlags(mIPv4Address != null, mMulticastFilter);
- }
-
- @GuardedBy("this")
- private void logApfProgramEventLocked(long now) {
- if (mLastInstallEvent == null) {
- return;
- }
- ApfProgramEvent.Builder ev = mLastInstallEvent;
- mLastInstallEvent = null;
- final long actualLifetime = now - mLastTimeInstalledProgram;
- ev.setActualLifetime(actualLifetime);
- if (actualLifetime < APF_PROGRAM_EVENT_LIFETIME_THRESHOLD) {
- return;
- }
- mMetricsLog.log(ev.build());
- }
-
- /**
- * Returns {@code true} if a new program should be installed because the current one dies soon.
- */
- private boolean shouldInstallnewProgram() {
- long expiry = mLastTimeInstalledProgram + mLastInstalledProgramMinLifetime;
- return expiry < currentTimeSeconds() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING;
- }
-
- private void hexDump(String msg, byte[] packet, int length) {
- log(msg + HexDump.toHexString(packet, 0, length, false /* lowercase */));
- }
-
- @GuardedBy("this")
- private void purgeExpiredRasLocked() {
- for (int i = 0; i < mRas.size();) {
- if (mRas.get(i).isExpired()) {
- log("Expiring " + mRas.get(i));
- mRas.remove(i);
- } else {
- i++;
- }
- }
- }
-
- /**
- * Process an RA packet, updating the list of known RAs and installing a new APF program
- * if the current APF program should be updated.
- * @return a ProcessRaResult enum describing what action was performed.
- */
- @VisibleForTesting
- synchronized ProcessRaResult processRa(byte[] packet, int length) {
- if (VDBG) hexDump("Read packet = ", packet, length);
-
- // Have we seen this RA before?
- for (int i = 0; i < mRas.size(); i++) {
- Ra ra = mRas.get(i);
- if (ra.matches(packet, length)) {
- if (VDBG) log("matched RA " + ra);
- // Update lifetimes.
- ra.mLastSeen = currentTimeSeconds();
- ra.mMinLifetime = ra.minLifetime(packet, length);
- ra.seenCount++;
-
- // Keep mRas in LRU order so as to prioritize generating filters for recently seen
- // RAs. LRU prioritizes this because RA filters are generated in order from mRas
- // until the filter program exceeds the maximum filter program size allowed by the
- // chipset, so RAs appearing earlier in mRas are more likely to make it into the
- // filter program.
- // TODO: consider sorting the RAs in order of increasing expiry time as well.
- // Swap to front of array.
- mRas.add(0, mRas.remove(i));
-
- // If the current program doesn't expire for a while, don't update.
- if (shouldInstallnewProgram()) {
- installNewProgramLocked();
- return ProcessRaResult.UPDATE_EXPIRY;
- }
- return ProcessRaResult.MATCH;
- }
- }
- purgeExpiredRasLocked();
- // TODO: figure out how to proceed when we've received more then MAX_RAS RAs.
- if (mRas.size() >= MAX_RAS) {
- return ProcessRaResult.DROPPED;
- }
- final Ra ra;
- try {
- ra = new Ra(packet, length);
- } catch (Exception e) {
- Log.e(TAG, "Error parsing RA", e);
- return ProcessRaResult.PARSE_ERROR;
- }
- // Ignore 0 lifetime RAs.
- if (ra.isExpired()) {
- return ProcessRaResult.ZERO_LIFETIME;
- }
- log("Adding " + ra);
- mRas.add(ra);
- installNewProgramLocked();
- return ProcessRaResult.UPDATE_NEW_RA;
- }
-
- /**
- * Create an {@link ApfFilter} if {@code apfCapabilities} indicates support for packet
- * filtering using APF programs.
- */
- public static ApfFilter maybeCreate(Context context, ApfConfiguration config,
- InterfaceParams ifParams, IpClientCallbacksWrapper ipClientCallback) {
- if (context == null || config == null || ifParams == null) return null;
- ApfCapabilities apfCapabilities = config.apfCapabilities;
- if (apfCapabilities == null) return null;
- if (apfCapabilities.apfVersionSupported == 0) return null;
- if (apfCapabilities.maximumApfProgramSize < 512) {
- Log.e(TAG, "Unacceptably small APF limit: " + apfCapabilities.maximumApfProgramSize);
- return null;
- }
- // For now only support generating programs for Ethernet frames. If this restriction is
- // lifted:
- // 1. the program generator will need its offsets adjusted.
- // 2. the packet filter attached to our packet socket will need its offset adjusted.
- if (apfCapabilities.apfPacketFormat != ARPHRD_ETHER) return null;
- if (!ApfGenerator.supportsVersion(apfCapabilities.apfVersionSupported)) {
- Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
- return null;
- }
-
- return new ApfFilter(context, config, ifParams, ipClientCallback, new IpConnectivityLog());
- }
-
- public synchronized void shutdown() {
- if (mReceiveThread != null) {
- log("shutting down");
- mReceiveThread.halt(); // Also closes socket.
- mReceiveThread = null;
- }
- mRas.clear();
- mContext.unregisterReceiver(mDeviceIdleReceiver);
- }
-
- public synchronized void setMulticastFilter(boolean isEnabled) {
- if (mMulticastFilter == isEnabled) return;
- mMulticastFilter = isEnabled;
- if (!isEnabled) {
- mNumProgramUpdatesAllowingMulticast++;
- }
- installNewProgramLocked();
- }
-
- @VisibleForTesting
- public synchronized void setDozeMode(boolean isEnabled) {
- if (mInDozeMode == isEnabled) return;
- mInDozeMode = isEnabled;
- installNewProgramLocked();
- }
-
- /** Find the single IPv4 LinkAddress if there is one, otherwise return null. */
- private static LinkAddress findIPv4LinkAddress(LinkProperties lp) {
- LinkAddress ipv4Address = null;
- for (LinkAddress address : lp.getLinkAddresses()) {
- if (!(address.getAddress() instanceof Inet4Address)) {
- continue;
- }
- if (ipv4Address != null && !ipv4Address.isSameAddressAs(address)) {
- // More than one IPv4 address, abort.
- return null;
- }
- ipv4Address = address;
- }
- return ipv4Address;
- }
-
- public synchronized void setLinkProperties(LinkProperties lp) {
- // NOTE: Do not keep a copy of LinkProperties as it would further duplicate state.
- final LinkAddress ipv4Address = findIPv4LinkAddress(lp);
- final byte[] addr = (ipv4Address != null) ? ipv4Address.getAddress().getAddress() : null;
- final int prefix = (ipv4Address != null) ? ipv4Address.getPrefixLength() : 0;
- if ((prefix == mIPv4PrefixLength) && Arrays.equals(addr, mIPv4Address)) {
- return;
- }
- mIPv4Address = addr;
- mIPv4PrefixLength = prefix;
- installNewProgramLocked();
- }
-
- /**
- * Add TCP keepalive ack packet filter.
- * This will add a filter to drop acks to the keepalive packet passed as an argument.
- *
- * @param slot The index used to access the filter.
- * @param sentKeepalivePacket The attributes of the sent keepalive packet.
- */
- public synchronized void addTcpKeepalivePacketFilter(final int slot,
- final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
- log("Adding keepalive ack(" + slot + ")");
- if (null != mKeepalivePackets.get(slot)) {
- throw new IllegalArgumentException("Keepalive slot " + slot + " is occupied");
- }
- final int ipVersion = sentKeepalivePacket.srcAddress.length == 4 ? 4 : 6;
- mKeepalivePackets.put(slot, (ipVersion == 4)
- ? new TcpKeepaliveAckV4(sentKeepalivePacket)
- : new TcpKeepaliveAckV6(sentKeepalivePacket));
- installNewProgramLocked();
- }
-
- /**
- * Add NAT-T keepalive packet filter.
- * This will add a filter to drop NAT-T keepalive packet which is passed as an argument.
- *
- * @param slot The index used to access the filter.
- * @param sentKeepalivePacket The attributes of the sent keepalive packet.
- */
- public synchronized void addNattKeepalivePacketFilter(final int slot,
- final NattKeepalivePacketDataParcelable sentKeepalivePacket) {
- log("Adding NAT-T keepalive packet(" + slot + ")");
- if (null != mKeepalivePackets.get(slot)) {
- throw new IllegalArgumentException("NAT-T Keepalive slot " + slot + " is occupied");
- }
- if (sentKeepalivePacket.srcAddress.length != 4) {
- throw new IllegalArgumentException("NAT-T keepalive is only supported on IPv4");
- }
- mKeepalivePackets.put(slot, new NattKeepaliveResponse(sentKeepalivePacket));
- installNewProgramLocked();
- }
-
- /**
- * Remove keepalive packet filter.
- *
- * @param slot The index used to access the filter.
- */
- public synchronized void removeKeepalivePacketFilter(int slot) {
- log("Removing keepalive packet(" + slot + ")");
- mKeepalivePackets.remove(slot);
- installNewProgramLocked();
- }
-
- static public long counterValue(byte[] data, Counter counter)
- throws ArrayIndexOutOfBoundsException {
- // Follow the same wrap-around addressing scheme of the interpreter.
- int offset = counter.offset();
- if (offset < 0) {
- offset = data.length + offset;
- }
-
- // Decode 32bit big-endian integer into a long so we can count up beyond 2^31.
- long value = 0;
- for (int i = 0; i < 4; i++) {
- value = value << 8 | (data[offset] & 0xFF);
- offset++;
- }
- return value;
- }
-
- public synchronized void dump(IndentingPrintWriter pw) {
- pw.println("Capabilities: " + mApfCapabilities);
- pw.println("Receive thread: " + (mReceiveThread != null ? "RUNNING" : "STOPPED"));
- pw.println("Multicast: " + (mMulticastFilter ? "DROP" : "ALLOW"));
- try {
- pw.println("IPv4 address: " + InetAddress.getByAddress(mIPv4Address).getHostAddress());
- } catch (UnknownHostException|NullPointerException e) {}
-
- if (mLastTimeInstalledProgram == 0) {
- pw.println("No program installed.");
- return;
- }
- pw.println("Program updates: " + mNumProgramUpdates);
- pw.println(String.format(
- "Last program length %d, installed %ds ago, lifetime %ds",
- mLastInstalledProgram.length, currentTimeSeconds() - mLastTimeInstalledProgram,
- mLastInstalledProgramMinLifetime));
-
- pw.println("RA filters:");
- pw.increaseIndent();
- for (Ra ra: mRas) {
- pw.println(ra);
- pw.increaseIndent();
- pw.println(String.format(
- "Seen: %d, last %ds ago", ra.seenCount, currentTimeSeconds() - ra.mLastSeen));
- if (DBG) {
- pw.println("Last match:");
- pw.increaseIndent();
- pw.println(ra.getLastMatchingPacket());
- pw.decreaseIndent();
- }
- pw.decreaseIndent();
- }
- pw.decreaseIndent();
-
- pw.println("TCP Keepalive filters:");
- pw.increaseIndent();
- for (int i = 0; i < mKeepalivePackets.size(); ++i) {
- final KeepalivePacket keepalivePacket = mKeepalivePackets.valueAt(i);
- if (keepalivePacket instanceof TcpKeepaliveAck) {
- pw.print("Slot ");
- pw.print(mKeepalivePackets.keyAt(i));
- pw.print(": ");
- pw.println(keepalivePacket);
- }
- }
- pw.decreaseIndent();
-
- pw.println("NAT-T Keepalive filters:");
- pw.increaseIndent();
- for (int i = 0; i < mKeepalivePackets.size(); ++i) {
- final KeepalivePacket keepalivePacket = mKeepalivePackets.valueAt(i);
- if (keepalivePacket instanceof NattKeepaliveResponse) {
- pw.print("Slot ");
- pw.print(mKeepalivePackets.keyAt(i));
- pw.print(": ");
- pw.println(keepalivePacket);
- }
- }
- pw.decreaseIndent();
-
- if (DBG) {
- pw.println("Last program:");
- pw.increaseIndent();
- pw.println(HexDump.toHexString(mLastInstalledProgram, false /* lowercase */));
- pw.decreaseIndent();
- }
-
- pw.println("APF packet counters: ");
- pw.increaseIndent();
- if (!mApfCapabilities.hasDataAccess()) {
- pw.println("APF counters not supported");
- } else if (mDataSnapshot == null) {
- pw.println("No last snapshot.");
- } else {
- try {
- Counter[] counters = Counter.class.getEnumConstants();
- for (Counter c : Arrays.asList(counters).subList(1, counters.length)) {
- long value = counterValue(mDataSnapshot, c);
- // Only print non-zero counters
- if (value != 0) {
- pw.println(c.toString() + ": " + value);
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- pw.println("Uh-oh: " + e);
- }
- if (VDBG) {
- pw.println("Raw data dump: ");
- pw.println(HexDump.dumpHexString(mDataSnapshot));
- }
- }
- pw.decreaseIndent();
- }
-
- // TODO: move to android.net.NetworkUtils
- @VisibleForTesting
- public static int ipv4BroadcastAddress(byte[] addrBytes, int prefixLength) {
- return bytesToBEInt(addrBytes) | (int) (Integer.toUnsignedLong(-1) >>> prefixLength);
- }
-
- private static int uint8(byte b) {
- return b & 0xff;
- }
-
- private static int getUint16(ByteBuffer buffer, int position) {
- return buffer.getShort(position) & 0xffff;
- }
-
- private static long getUint32(ByteBuffer buffer, int position) {
- return Integer.toUnsignedLong(buffer.getInt(position));
- }
-
- private static int getUint8(ByteBuffer buffer, int position) {
- return uint8(buffer.get(position));
- }
-
- private static int bytesToBEInt(byte[] bytes) {
- return (uint8(bytes[0]) << 24)
- + (uint8(bytes[1]) << 16)
- + (uint8(bytes[2]) << 8)
- + (uint8(bytes[3]));
- }
-
- private static byte[] concatArrays(final byte[]... arr) {
- int size = 0;
- for (byte[] a : arr) {
- size += a.length;
- }
- final byte[] result = new byte[size];
- int offset = 0;
- for (byte[] a : arr) {
- System.arraycopy(a, 0, result, offset, a.length);
- offset += a.length;
- }
- return result;
- }
-}
diff --git a/packages/NetworkStack/src/android/net/apf/ApfGenerator.java b/packages/NetworkStack/src/android/net/apf/ApfGenerator.java
deleted file mode 100644
index 44ce2db..0000000
--- a/packages/NetworkStack/src/android/net/apf/ApfGenerator.java
+++ /dev/null
@@ -1,937 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.apf;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-/**
- * APF assembler/generator. A tool for generating an APF program.
- *
- * Call add*() functions to add instructions to the program, then call
- * {@link generate} to get the APF bytecode for the program.
- *
- * @hide
- */
-public class ApfGenerator {
- /**
- * This exception is thrown when an attempt is made to generate an illegal instruction.
- */
- public static class IllegalInstructionException extends Exception {
- IllegalInstructionException(String msg) {
- super(msg);
- }
- }
- private enum Opcodes {
- LABEL(-1),
- LDB(1), // Load 1 byte from immediate offset, e.g. "ldb R0, [5]"
- LDH(2), // Load 2 bytes from immediate offset, e.g. "ldh R0, [5]"
- LDW(3), // Load 4 bytes from immediate offset, e.g. "ldw R0, [5]"
- LDBX(4), // Load 1 byte from immediate offset plus register, e.g. "ldbx R0, [5]R0"
- LDHX(5), // Load 2 byte from immediate offset plus register, e.g. "ldhx R0, [5]R0"
- LDWX(6), // Load 4 byte from immediate offset plus register, e.g. "ldwx R0, [5]R0"
- ADD(7), // Add, e.g. "add R0,5"
- MUL(8), // Multiply, e.g. "mul R0,5"
- DIV(9), // Divide, e.g. "div R0,5"
- AND(10), // And, e.g. "and R0,5"
- OR(11), // Or, e.g. "or R0,5"
- SH(12), // Left shift, e.g, "sh R0, 5" or "sh R0, -5" (shifts right)
- LI(13), // Load immediate, e.g. "li R0,5" (immediate encoded as signed value)
- JMP(14), // Jump, e.g. "jmp label"
- JEQ(15), // Compare equal and branch, e.g. "jeq R0,5,label"
- JNE(16), // Compare not equal and branch, e.g. "jne R0,5,label"
- JGT(17), // Compare greater than and branch, e.g. "jgt R0,5,label"
- JLT(18), // Compare less than and branch, e.g. "jlt R0,5,label"
- JSET(19), // Compare any bits set and branch, e.g. "jset R0,5,label"
- JNEBS(20), // Compare not equal byte sequence, e.g. "jnebs R0,5,label,0x1122334455"
- EXT(21), // Followed by immediate indicating ExtendedOpcodes.
- LDDW(22), // Load 4 bytes from data memory address (register + immediate): "lddw R0, [5]R1"
- STDW(23); // Store 4 bytes to data memory address (register + immediate): "stdw R0, [5]R1"
-
- final int value;
-
- private Opcodes(int value) {
- this.value = value;
- }
- }
- // Extended opcodes. Primary opcode is Opcodes.EXT. ExtendedOpcodes are encoded in the immediate
- // field.
- private enum ExtendedOpcodes {
- LDM(0), // Load from memory, e.g. "ldm R0,5"
- STM(16), // Store to memory, e.g. "stm R0,5"
- NOT(32), // Not, e.g. "not R0"
- NEG(33), // Negate, e.g. "neg R0"
- SWAP(34), // Swap, e.g. "swap R0,R1"
- MOVE(35); // Move, e.g. "move R0,R1"
-
- final int value;
-
- private ExtendedOpcodes(int value) {
- this.value = value;
- }
- }
- public enum Register {
- R0(0),
- R1(1);
-
- final int value;
-
- private Register(int value) {
- this.value = value;
- }
- }
- private class Instruction {
- private final byte mOpcode; // A "Opcode" value.
- private final byte mRegister; // A "Register" value.
- private boolean mHasImm;
- private byte mImmSize;
- private boolean mImmSigned;
- private int mImm;
- // When mOpcode is a jump:
- private byte mTargetLabelSize;
- private String mTargetLabel;
- // When mOpcode == Opcodes.LABEL:
- private String mLabel;
- // When mOpcode == Opcodes.JNEBS:
- private byte[] mCompareBytes;
- // Offset in bytes from the beginning of this program. Set by {@link ApfGenerator#generate}.
- int offset;
-
- Instruction(Opcodes opcode, Register register) {
- mOpcode = (byte)opcode.value;
- mRegister = (byte)register.value;
- }
-
- Instruction(Opcodes opcode) {
- this(opcode, Register.R0);
- }
-
- void setImm(int imm, boolean signed) {
- mHasImm = true;
- mImm = imm;
- mImmSigned = signed;
- mImmSize = calculateImmSize(imm, signed);
- }
-
- void setUnsignedImm(int imm) {
- setImm(imm, false);
- }
-
- void setSignedImm(int imm) {
- setImm(imm, true);
- }
-
- void setLabel(String label) throws IllegalInstructionException {
- if (mLabels.containsKey(label)) {
- throw new IllegalInstructionException("duplicate label " + label);
- }
- if (mOpcode != Opcodes.LABEL.value) {
- throw new IllegalStateException("adding label to non-label instruction");
- }
- mLabel = label;
- mLabels.put(label, this);
- }
-
- void setTargetLabel(String label) {
- mTargetLabel = label;
- mTargetLabelSize = 4; // May shrink later on in generate().
- }
-
- void setCompareBytes(byte[] bytes) {
- if (mOpcode != Opcodes.JNEBS.value) {
- throw new IllegalStateException("adding compare bytes to non-JNEBS instruction");
- }
- mCompareBytes = bytes;
- }
-
- /**
- * @return size of instruction in bytes.
- */
- int size() {
- if (mOpcode == Opcodes.LABEL.value) {
- return 0;
- }
- int size = 1;
- if (mHasImm) {
- size += generatedImmSize();
- }
- if (mTargetLabel != null) {
- size += generatedImmSize();
- }
- if (mCompareBytes != null) {
- size += mCompareBytes.length;
- }
- return size;
- }
-
- /**
- * Resize immediate value field so that it's only as big as required to
- * contain the offset of the jump destination.
- * @return {@code true} if shrunk.
- */
- boolean shrink() throws IllegalInstructionException {
- if (mTargetLabel == null) {
- return false;
- }
- int oldSize = size();
- int oldTargetLabelSize = mTargetLabelSize;
- mTargetLabelSize = calculateImmSize(calculateTargetLabelOffset(), false);
- if (mTargetLabelSize > oldTargetLabelSize) {
- throw new IllegalStateException("instruction grew");
- }
- return size() < oldSize;
- }
-
- /**
- * Assemble value for instruction size field.
- */
- private byte generateImmSizeField() {
- byte immSize = generatedImmSize();
- // Encode size field to fit in 2 bits: 0->0, 1->1, 2->2, 3->4.
- return immSize == 4 ? 3 : immSize;
- }
-
- /**
- * Assemble first byte of generated instruction.
- */
- private byte generateInstructionByte() {
- byte sizeField = generateImmSizeField();
- return (byte)((mOpcode << 3) | (sizeField << 1) | mRegister);
- }
-
- /**
- * Write {@code value} at offset {@code writingOffset} into {@code bytecode}.
- * {@link generatedImmSize} bytes are written. {@code value} is truncated to
- * {@code generatedImmSize} bytes. {@code value} is treated simply as a
- * 32-bit value, so unsigned values should be zero extended and the truncation
- * should simply throw away their zero-ed upper bits, and signed values should
- * be sign extended and the truncation should simply throw away their signed
- * upper bits.
- */
- private int writeValue(int value, byte[] bytecode, int writingOffset) {
- for (int i = generatedImmSize() - 1; i >= 0; i--) {
- bytecode[writingOffset++] = (byte)((value >> (i * 8)) & 255);
- }
- return writingOffset;
- }
-
- /**
- * Generate bytecode for this instruction at offset {@link offset}.
- */
- void generate(byte[] bytecode) throws IllegalInstructionException {
- if (mOpcode == Opcodes.LABEL.value) {
- return;
- }
- int writingOffset = offset;
- bytecode[writingOffset++] = generateInstructionByte();
- if (mTargetLabel != null) {
- writingOffset = writeValue(calculateTargetLabelOffset(), bytecode, writingOffset);
- }
- if (mHasImm) {
- writingOffset = writeValue(mImm, bytecode, writingOffset);
- }
- if (mCompareBytes != null) {
- System.arraycopy(mCompareBytes, 0, bytecode, writingOffset, mCompareBytes.length);
- writingOffset += mCompareBytes.length;
- }
- if ((writingOffset - offset) != size()) {
- throw new IllegalStateException("wrote " + (writingOffset - offset) +
- " but should have written " + size());
- }
- }
-
- /**
- * Calculate the size of either the immediate field or the target label field, if either is
- * present. Most instructions have either an immediate or a target label field, but for the
- * instructions that have both, the size of the target label field must be the same as the
- * size of the immediate field, because there is only one length field in the instruction
- * byte, hence why this function simply takes the maximum of the two sizes, so neither is
- * truncated.
- */
- private byte generatedImmSize() {
- return mImmSize > mTargetLabelSize ? mImmSize : mTargetLabelSize;
- }
-
- private int calculateTargetLabelOffset() throws IllegalInstructionException {
- Instruction targetLabelInstruction;
- if (mTargetLabel == DROP_LABEL) {
- targetLabelInstruction = mDropLabel;
- } else if (mTargetLabel == PASS_LABEL) {
- targetLabelInstruction = mPassLabel;
- } else {
- targetLabelInstruction = mLabels.get(mTargetLabel);
- }
- if (targetLabelInstruction == null) {
- throw new IllegalInstructionException("label not found: " + mTargetLabel);
- }
- // Calculate distance from end of this instruction to instruction.offset.
- final int targetLabelOffset = targetLabelInstruction.offset - (offset + size());
- if (targetLabelOffset < 0) {
- throw new IllegalInstructionException("backward branches disallowed; label: " +
- mTargetLabel);
- }
- return targetLabelOffset;
- }
-
- private byte calculateImmSize(int imm, boolean signed) {
- if (imm == 0) {
- return 0;
- }
- if (signed && (imm >= -128 && imm <= 127) ||
- !signed && (imm >= 0 && imm <= 255)) {
- return 1;
- }
- if (signed && (imm >= -32768 && imm <= 32767) ||
- !signed && (imm >= 0 && imm <= 65535)) {
- return 2;
- }
- return 4;
- }
- }
-
- /**
- * Jump to this label to terminate the program and indicate the packet
- * should be dropped.
- */
- public static final String DROP_LABEL = "__DROP__";
-
- /**
- * Jump to this label to terminate the program and indicate the packet
- * should be passed to the AP.
- */
- public static final String PASS_LABEL = "__PASS__";
-
- /**
- * Number of memory slots available for access via APF stores to memory and loads from memory.
- * The memory slots are numbered 0 to {@code MEMORY_SLOTS} - 1. This must be kept in sync with
- * the APF interpreter.
- */
- public static final int MEMORY_SLOTS = 16;
-
- /**
- * Memory slot number that is prefilled with the IPv4 header length.
- * Note that this memory slot may be overwritten by a program that
- * executes stores to this memory slot. This must be kept in sync with
- * the APF interpreter.
- */
- public static final int IPV4_HEADER_SIZE_MEMORY_SLOT = 13;
-
- /**
- * Memory slot number that is prefilled with the size of the packet being filtered in bytes.
- * Note that this memory slot may be overwritten by a program that
- * executes stores to this memory slot. This must be kept in sync with the APF interpreter.
- */
- public static final int PACKET_SIZE_MEMORY_SLOT = 14;
-
- /**
- * Memory slot number that is prefilled with the age of the filter in seconds. The age of the
- * filter is the time since the filter was installed until now.
- * Note that this memory slot may be overwritten by a program that
- * executes stores to this memory slot. This must be kept in sync with the APF interpreter.
- */
- public static final int FILTER_AGE_MEMORY_SLOT = 15;
-
- /**
- * First memory slot containing prefilled values. Can be used in range comparisons to determine
- * if memory slot index is within prefilled slots.
- */
- public static final int FIRST_PREFILLED_MEMORY_SLOT = IPV4_HEADER_SIZE_MEMORY_SLOT;
-
- /**
- * Last memory slot containing prefilled values. Can be used in range comparisons to determine
- * if memory slot index is within prefilled slots.
- */
- public static final int LAST_PREFILLED_MEMORY_SLOT = FILTER_AGE_MEMORY_SLOT;
-
- // This version number syncs up with APF_VERSION in hardware/google/apf/apf_interpreter.h
- private static final int MIN_APF_VERSION = 2;
-
- private final ArrayList<Instruction> mInstructions = new ArrayList<Instruction>();
- private final HashMap<String, Instruction> mLabels = new HashMap<String, Instruction>();
- private final Instruction mDropLabel = new Instruction(Opcodes.LABEL);
- private final Instruction mPassLabel = new Instruction(Opcodes.LABEL);
- private final int mVersion;
- private boolean mGenerated;
-
- /**
- * Creates an ApfGenerator instance which is able to emit instructions for the specified
- * {@code version} of the APF interpreter. Throws {@code IllegalInstructionException} if
- * the requested version is unsupported.
- */
- ApfGenerator(int version) throws IllegalInstructionException {
- mVersion = version;
- requireApfVersion(MIN_APF_VERSION);
- }
-
- /**
- * Returns true if the ApfGenerator supports the specified {@code version}, otherwise false.
- */
- public static boolean supportsVersion(int version) {
- return version >= MIN_APF_VERSION;
- }
-
- private void requireApfVersion(int minimumVersion) throws IllegalInstructionException {
- if (mVersion < minimumVersion) {
- throw new IllegalInstructionException("Requires APF >= " + minimumVersion);
- }
- }
-
- private void addInstruction(Instruction instruction) {
- if (mGenerated) {
- throw new IllegalStateException("Program already generated");
- }
- mInstructions.add(instruction);
- }
-
- /**
- * Define a label at the current end of the program. Jumps can jump to this label. Labels are
- * their own separate instructions, though with size 0. This facilitates having labels with
- * no corresponding code to execute, for example a label at the end of a program. For example
- * an {@link ApfGenerator} might be passed to a function that adds a filter like so:
- * <pre>
- * load from packet
- * compare loaded data, jump if not equal to "next_filter"
- * load from packet
- * compare loaded data, jump if not equal to "next_filter"
- * jump to drop label
- * define "next_filter" here
- * </pre>
- * In this case "next_filter" may not have any generated code associated with it.
- */
- public ApfGenerator defineLabel(String name) throws IllegalInstructionException {
- Instruction instruction = new Instruction(Opcodes.LABEL);
- instruction.setLabel(name);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an unconditional jump instruction to the end of the program.
- */
- public ApfGenerator addJump(String target) {
- Instruction instruction = new Instruction(Opcodes.JMP);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load the byte at offset {@code offset}
- * bytes from the beginning of the packet into {@code register}.
- */
- public ApfGenerator addLoad8(Register register, int offset) {
- Instruction instruction = new Instruction(Opcodes.LDB, register);
- instruction.setUnsignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load 16-bits at offset {@code offset}
- * bytes from the beginning of the packet into {@code register}.
- */
- public ApfGenerator addLoad16(Register register, int offset) {
- Instruction instruction = new Instruction(Opcodes.LDH, register);
- instruction.setUnsignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load 32-bits at offset {@code offset}
- * bytes from the beginning of the packet into {@code register}.
- */
- public ApfGenerator addLoad32(Register register, int offset) {
- Instruction instruction = new Instruction(Opcodes.LDW, register);
- instruction.setUnsignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load a byte from the packet into
- * {@code register}. The offset of the loaded byte from the beginning of the packet is
- * the sum of {@code offset} and the value in register R1.
- */
- public ApfGenerator addLoad8Indexed(Register register, int offset) {
- Instruction instruction = new Instruction(Opcodes.LDBX, register);
- instruction.setUnsignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load 16-bits from the packet into
- * {@code register}. The offset of the loaded 16-bits from the beginning of the packet is
- * the sum of {@code offset} and the value in register R1.
- */
- public ApfGenerator addLoad16Indexed(Register register, int offset) {
- Instruction instruction = new Instruction(Opcodes.LDHX, register);
- instruction.setUnsignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load 32-bits from the packet into
- * {@code register}. The offset of the loaded 32-bits from the beginning of the packet is
- * the sum of {@code offset} and the value in register R1.
- */
- public ApfGenerator addLoad32Indexed(Register register, int offset) {
- Instruction instruction = new Instruction(Opcodes.LDWX, register);
- instruction.setUnsignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to add {@code value} to register R0.
- */
- public ApfGenerator addAdd(int value) {
- Instruction instruction = new Instruction(Opcodes.ADD);
- instruction.setSignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to multiply register R0 by {@code value}.
- */
- public ApfGenerator addMul(int value) {
- Instruction instruction = new Instruction(Opcodes.MUL);
- instruction.setSignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to divide register R0 by {@code value}.
- */
- public ApfGenerator addDiv(int value) {
- Instruction instruction = new Instruction(Opcodes.DIV);
- instruction.setSignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to logically and register R0 with {@code value}.
- */
- public ApfGenerator addAnd(int value) {
- Instruction instruction = new Instruction(Opcodes.AND);
- instruction.setUnsignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to logically or register R0 with {@code value}.
- */
- public ApfGenerator addOr(int value) {
- Instruction instruction = new Instruction(Opcodes.OR);
- instruction.setUnsignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to shift left register R0 by {@code value} bits.
- */
- public ApfGenerator addLeftShift(int value) {
- Instruction instruction = new Instruction(Opcodes.SH);
- instruction.setSignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to shift right register R0 by {@code value}
- * bits.
- */
- public ApfGenerator addRightShift(int value) {
- Instruction instruction = new Instruction(Opcodes.SH);
- instruction.setSignedImm(-value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to add register R1 to register R0.
- */
- public ApfGenerator addAddR1() {
- Instruction instruction = new Instruction(Opcodes.ADD, Register.R1);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to multiply register R0 by register R1.
- */
- public ApfGenerator addMulR1() {
- Instruction instruction = new Instruction(Opcodes.MUL, Register.R1);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to divide register R0 by register R1.
- */
- public ApfGenerator addDivR1() {
- Instruction instruction = new Instruction(Opcodes.DIV, Register.R1);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to logically and register R0 with register R1
- * and store the result back into register R0.
- */
- public ApfGenerator addAndR1() {
- Instruction instruction = new Instruction(Opcodes.AND, Register.R1);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to logically or register R0 with register R1
- * and store the result back into register R0.
- */
- public ApfGenerator addOrR1() {
- Instruction instruction = new Instruction(Opcodes.OR, Register.R1);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to shift register R0 left by the value in
- * register R1.
- */
- public ApfGenerator addLeftShiftR1() {
- Instruction instruction = new Instruction(Opcodes.SH, Register.R1);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to move {@code value} into {@code register}.
- */
- public ApfGenerator addLoadImmediate(Register register, int value) {
- Instruction instruction = new Instruction(Opcodes.LI, register);
- instruction.setSignedImm(value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value equals {@code value}.
- */
- public ApfGenerator addJumpIfR0Equals(int value, String target) {
- Instruction instruction = new Instruction(Opcodes.JEQ);
- instruction.setUnsignedImm(value);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value does not equal {@code value}.
- */
- public ApfGenerator addJumpIfR0NotEquals(int value, String target) {
- Instruction instruction = new Instruction(Opcodes.JNE);
- instruction.setUnsignedImm(value);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value is greater than {@code value}.
- */
- public ApfGenerator addJumpIfR0GreaterThan(int value, String target) {
- Instruction instruction = new Instruction(Opcodes.JGT);
- instruction.setUnsignedImm(value);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value is less than {@code value}.
- */
- public ApfGenerator addJumpIfR0LessThan(int value, String target) {
- Instruction instruction = new Instruction(Opcodes.JLT);
- instruction.setUnsignedImm(value);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value has any bits set that are also set in {@code value}.
- */
- public ApfGenerator addJumpIfR0AnyBitsSet(int value, String target) {
- Instruction instruction = new Instruction(Opcodes.JSET);
- instruction.setUnsignedImm(value);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value equals register R1's value.
- */
- public ApfGenerator addJumpIfR0EqualsR1(String target) {
- Instruction instruction = new Instruction(Opcodes.JEQ, Register.R1);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value does not equal register R1's value.
- */
- public ApfGenerator addJumpIfR0NotEqualsR1(String target) {
- Instruction instruction = new Instruction(Opcodes.JNE, Register.R1);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value is greater than register R1's value.
- */
- public ApfGenerator addJumpIfR0GreaterThanR1(String target) {
- Instruction instruction = new Instruction(Opcodes.JGT, Register.R1);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value is less than register R1's value.
- */
- public ApfGenerator addJumpIfR0LessThanR1(String target) {
- Instruction instruction = new Instruction(Opcodes.JLT, Register.R1);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if register R0's
- * value has any bits set that are also set in R1's value.
- */
- public ApfGenerator addJumpIfR0AnyBitsSetR1(String target) {
- Instruction instruction = new Instruction(Opcodes.JSET, Register.R1);
- instruction.setTargetLabel(target);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to jump to {@code target} if the bytes of the
- * packet at an offset specified by {@code register} match {@code bytes}.
- */
- public ApfGenerator addJumpIfBytesNotEqual(Register register, byte[] bytes, String target)
- throws IllegalInstructionException {
- if (register == Register.R1) {
- throw new IllegalInstructionException("JNEBS fails with R1");
- }
- Instruction instruction = new Instruction(Opcodes.JNEBS, register);
- instruction.setUnsignedImm(bytes.length);
- instruction.setTargetLabel(target);
- instruction.setCompareBytes(bytes);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load memory slot {@code slot} into
- * {@code register}.
- */
- public ApfGenerator addLoadFromMemory(Register register, int slot)
- throws IllegalInstructionException {
- if (slot < 0 || slot > (MEMORY_SLOTS - 1)) {
- throw new IllegalInstructionException("illegal memory slot number: " + slot);
- }
- Instruction instruction = new Instruction(Opcodes.EXT, register);
- instruction.setUnsignedImm(ExtendedOpcodes.LDM.value + slot);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to store {@code register} into memory slot
- * {@code slot}.
- */
- public ApfGenerator addStoreToMemory(Register register, int slot)
- throws IllegalInstructionException {
- if (slot < 0 || slot > (MEMORY_SLOTS - 1)) {
- throw new IllegalInstructionException("illegal memory slot number: " + slot);
- }
- Instruction instruction = new Instruction(Opcodes.EXT, register);
- instruction.setUnsignedImm(ExtendedOpcodes.STM.value + slot);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to logically not {@code register}.
- */
- public ApfGenerator addNot(Register register) {
- Instruction instruction = new Instruction(Opcodes.EXT, register);
- instruction.setUnsignedImm(ExtendedOpcodes.NOT.value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to negate {@code register}.
- */
- public ApfGenerator addNeg(Register register) {
- Instruction instruction = new Instruction(Opcodes.EXT, register);
- instruction.setUnsignedImm(ExtendedOpcodes.NEG.value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to swap the values in register R0 and register R1.
- */
- public ApfGenerator addSwap() {
- Instruction instruction = new Instruction(Opcodes.EXT);
- instruction.setUnsignedImm(ExtendedOpcodes.SWAP.value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to move the value into
- * {@code register} from the other register.
- */
- public ApfGenerator addMove(Register register) {
- Instruction instruction = new Instruction(Opcodes.EXT, register);
- instruction.setUnsignedImm(ExtendedOpcodes.MOVE.value);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to load 32 bits from the data memory into
- * {@code register}. The source address is computed by adding the signed immediate
- * @{code offset} to the other register.
- * Requires APF v3 or greater.
- */
- public ApfGenerator addLoadData(Register destinationRegister, int offset)
- throws IllegalInstructionException {
- requireApfVersion(3);
- Instruction instruction = new Instruction(Opcodes.LDDW, destinationRegister);
- instruction.setSignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Add an instruction to the end of the program to store 32 bits from {@code register} into the
- * data memory. The destination address is computed by adding the signed immediate
- * @{code offset} to the other register.
- * Requires APF v3 or greater.
- */
- public ApfGenerator addStoreData(Register sourceRegister, int offset)
- throws IllegalInstructionException {
- requireApfVersion(3);
- Instruction instruction = new Instruction(Opcodes.STDW, sourceRegister);
- instruction.setSignedImm(offset);
- addInstruction(instruction);
- return this;
- }
-
- /**
- * Updates instruction offset fields using latest instruction sizes.
- * @return current program length in bytes.
- */
- private int updateInstructionOffsets() {
- int offset = 0;
- for (Instruction instruction : mInstructions) {
- instruction.offset = offset;
- offset += instruction.size();
- }
- return offset;
- }
-
- /**
- * Returns an overestimate of the size of the generated program. {@link #generate} may return
- * a program that is smaller.
- */
- public int programLengthOverEstimate() {
- return updateInstructionOffsets();
- }
-
- /**
- * Generate the bytecode for the APF program.
- * @return the bytecode.
- * @throws IllegalStateException if a label is referenced but not defined.
- */
- public byte[] generate() throws IllegalInstructionException {
- // Enforce that we can only generate once because we cannot unshrink instructions and
- // PASS/DROP labels may move further away requiring unshrinking if we add further
- // instructions.
- if (mGenerated) {
- throw new IllegalStateException("Can only generate() once!");
- }
- mGenerated = true;
- int total_size;
- boolean shrunk;
- // Shrink the immediate value fields of instructions.
- // As we shrink the instructions some branch offset
- // fields may shrink also, thereby shrinking the
- // instructions further. Loop until we've reached the
- // minimum size. Rarely will this loop more than a few times.
- // Limit iterations to avoid O(n^2) behavior.
- int iterations_remaining = 10;
- do {
- total_size = updateInstructionOffsets();
- // Update drop and pass label offsets.
- mDropLabel.offset = total_size + 1;
- mPassLabel.offset = total_size;
- // Limit run-time in aberant circumstances.
- if (iterations_remaining-- == 0) break;
- // Attempt to shrink instructions.
- shrunk = false;
- for (Instruction instruction : mInstructions) {
- if (instruction.shrink()) {
- shrunk = true;
- }
- }
- } while (shrunk);
- // Generate bytecode for instructions.
- byte[] bytecode = new byte[total_size];
- for (Instruction instruction : mInstructions) {
- instruction.generate(bytecode);
- }
- return bytecode;
- }
-}
-
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpAckPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpAckPacket.java
deleted file mode 100644
index b2eb4e2..0000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpAckPacket.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-ACK packet.
- */
-class DhcpAckPacket extends DhcpPacket {
-
- /**
- * The address of the server which sent this packet.
- */
- private final Inet4Address mSrcIp;
-
- DhcpAckPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress,
- Inet4Address relayIp, Inet4Address clientIp, Inet4Address yourIp, byte[] clientMac) {
- super(transId, secs, clientIp, yourIp, serverAddress, relayIp, clientMac, broadcast);
- mBroadcast = broadcast;
- mSrcIp = serverAddress;
- }
-
- public String toString() {
- String s = super.toString();
- String dnsServers = " DNS servers: ";
-
- for (Inet4Address dnsServer: mDnsServers) {
- dnsServers += dnsServer.toString() + " ";
- }
-
- return s + " ACK: your new IP " + mYourIp +
- ", netmask " + mSubnetMask +
- ", gateways " + mGateways + dnsServers +
- ", lease time " + mLeaseTime;
- }
-
- /**
- * Fills in a packet with the requested ACK parameters.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- Inet4Address destIp = mBroadcast ? INADDR_BROADCAST : mYourIp;
- Inet4Address srcIp = mBroadcast ? INADDR_ANY : mSrcIp;
-
- fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result,
- DHCP_BOOTREPLY, mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds the optional parameters to the client-generated ACK packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_ACK);
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
-
- addCommonServerTlvs(buffer);
- addTlvEnd(buffer);
- }
-
- /**
- * Un-boxes an Integer, returning 0 if a null reference is supplied.
- */
- private static final int getInt(Integer v) {
- if (v == null) {
- return 0;
- } else {
- return v.intValue();
- }
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java
deleted file mode 100644
index ca6c17a..0000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java
+++ /dev/null
@@ -1,1070 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import static android.net.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS;
-import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER;
-import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME;
-import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME;
-import static android.net.dhcp.DhcpPacket.DHCP_MTU;
-import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME;
-import static android.net.dhcp.DhcpPacket.DHCP_RENEWAL_TIME;
-import static android.net.dhcp.DhcpPacket.DHCP_ROUTER;
-import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK;
-import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO;
-import static android.net.dhcp.DhcpPacket.INADDR_ANY;
-import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
-import static android.net.util.NetworkStackUtils.closeSocketQuietly;
-import static android.net.util.SocketUtils.makePacketSocketAddress;
-import static android.system.OsConstants.AF_INET;
-import static android.system.OsConstants.AF_PACKET;
-import static android.system.OsConstants.ETH_P_IP;
-import static android.system.OsConstants.IPPROTO_UDP;
-import static android.system.OsConstants.SOCK_DGRAM;
-import static android.system.OsConstants.SOCK_RAW;
-import static android.system.OsConstants.SOL_SOCKET;
-import static android.system.OsConstants.SO_BROADCAST;
-import static android.system.OsConstants.SO_RCVBUF;
-import static android.system.OsConstants.SO_REUSEADDR;
-
-import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
-
-import android.content.Context;
-import android.net.DhcpResults;
-import android.net.InetAddresses;
-import android.net.TrafficStats;
-import android.net.ip.IpClient;
-import android.net.metrics.DhcpClientEvent;
-import android.net.metrics.DhcpErrorEvent;
-import android.net.metrics.IpConnectivityLog;
-import android.net.util.InterfaceParams;
-import android.net.util.NetworkStackUtils;
-import android.net.util.SocketUtils;
-import android.os.Message;
-import android.os.SystemClock;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.internal.util.HexDump;
-import com.android.internal.util.MessageUtils;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-import com.android.internal.util.TrafficStatsConstants;
-import com.android.internal.util.WakeupMessage;
-import com.android.networkstack.R;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.Inet4Address;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Random;
-
-/**
- * A DHCPv4 client.
- *
- * Written to behave similarly to the DhcpStateMachine + dhcpcd 5.5.6 combination used in Android
- * 5.1 and below, as configured on Nexus 6. The interface is the same as DhcpStateMachine.
- *
- * TODO:
- *
- * - Exponential backoff when receiving NAKs (not specified by the RFC, but current behaviour).
- * - Support persisting lease state and support INIT-REBOOT. Android 5.1 does this, but it does not
- * do so correctly: instead of requesting the lease last obtained on a particular network (e.g., a
- * given SSID), it requests the last-leased IP address on the same interface, causing a delay if
- * the server NAKs or a timeout if it doesn't.
- *
- * Known differences from current behaviour:
- *
- * - Does not request the "static routes" option.
- * - Does not support BOOTP servers. DHCP has been around since 1993, should be everywhere now.
- * - Requests the "broadcast" option, but does nothing with it.
- * - Rejects invalid subnet masks such as 255.255.255.1 (current code treats that as 255.255.255.0).
- *
- * @hide
- */
-public class DhcpClient extends StateMachine {
-
- private static final String TAG = "DhcpClient";
- private static final boolean DBG = true;
- private static final boolean STATE_DBG = Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean MSG_DBG = Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean PACKET_DBG = Log.isLoggable(TAG, Log.DEBUG);
-
- // Metrics events: must be kept in sync with server-side aggregation code.
- /** Represents transitions from DhcpInitState to DhcpBoundState */
- private static final String EVENT_INITIAL_BOUND = "InitialBoundState";
- /** Represents transitions from and to DhcpBoundState via DhcpRenewingState */
- private static final String EVENT_RENEWING_BOUND = "RenewingBoundState";
-
- // Timers and timeouts.
- private static final int SECONDS = 1000;
- private static final int FIRST_TIMEOUT_MS = 2 * SECONDS;
- private static final int MAX_TIMEOUT_MS = 128 * SECONDS;
-
- // This is not strictly needed, since the client is asynchronous and implements exponential
- // backoff. It's maintained for backwards compatibility with the previous DHCP code, which was
- // a blocking operation with a 30-second timeout. We pick 36 seconds so we can send packets at
- // t=0, t=2, t=6, t=14, t=30, allowing for 10% jitter.
- private static final int DHCP_TIMEOUT_MS = 36 * SECONDS;
-
- // DhcpClient uses IpClient's handler.
- private static final int PUBLIC_BASE = IpClient.DHCPCLIENT_CMD_BASE;
-
- // Below constants are picked up by MessageUtils and exempt from ProGuard optimization.
- /* Commands from controller to start/stop DHCP */
- public static final int CMD_START_DHCP = PUBLIC_BASE + 1;
- public static final int CMD_STOP_DHCP = PUBLIC_BASE + 2;
-
- /* Notification from DHCP state machine prior to DHCP discovery/renewal */
- public static final int CMD_PRE_DHCP_ACTION = PUBLIC_BASE + 3;
- /* Notification from DHCP state machine post DHCP discovery/renewal. Indicates
- * success/failure */
- public static final int CMD_POST_DHCP_ACTION = PUBLIC_BASE + 4;
- /* Notification from DHCP state machine before quitting */
- public static final int CMD_ON_QUIT = PUBLIC_BASE + 5;
-
- /* Command from controller to indicate DHCP discovery/renewal can continue
- * after pre DHCP action is complete */
- public static final int CMD_PRE_DHCP_ACTION_COMPLETE = PUBLIC_BASE + 6;
-
- /* Command and event notification to/from IpManager requesting the setting
- * (or clearing) of an IPv4 LinkAddress.
- */
- public static final int CMD_CLEAR_LINKADDRESS = PUBLIC_BASE + 7;
- public static final int CMD_CONFIGURE_LINKADDRESS = PUBLIC_BASE + 8;
- public static final int EVENT_LINKADDRESS_CONFIGURED = PUBLIC_BASE + 9;
-
- /* Message.arg1 arguments to CMD_POST_DHCP_ACTION notification */
- public static final int DHCP_SUCCESS = 1;
- public static final int DHCP_FAILURE = 2;
-
- // Internal messages.
- private static final int PRIVATE_BASE = IpClient.DHCPCLIENT_CMD_BASE + 100;
- private static final int CMD_KICK = PRIVATE_BASE + 1;
- private static final int CMD_RECEIVED_PACKET = PRIVATE_BASE + 2;
- private static final int CMD_TIMEOUT = PRIVATE_BASE + 3;
- private static final int CMD_RENEW_DHCP = PRIVATE_BASE + 4;
- private static final int CMD_REBIND_DHCP = PRIVATE_BASE + 5;
- private static final int CMD_EXPIRE_DHCP = PRIVATE_BASE + 6;
-
- // For message logging.
- private static final Class[] sMessageClasses = { DhcpClient.class };
- private static final SparseArray<String> sMessageNames =
- MessageUtils.findMessageNames(sMessageClasses);
-
- // DHCP parameters that we request.
- /* package */ static final byte[] REQUESTED_PARAMS = new byte[] {
- DHCP_SUBNET_MASK,
- DHCP_ROUTER,
- DHCP_DNS_SERVER,
- DHCP_DOMAIN_NAME,
- DHCP_MTU,
- DHCP_BROADCAST_ADDRESS, // TODO: currently ignored.
- DHCP_LEASE_TIME,
- DHCP_RENEWAL_TIME,
- DHCP_REBINDING_TIME,
- DHCP_VENDOR_INFO,
- };
-
- // DHCP flag that means "yes, we support unicast."
- private static final boolean DO_UNICAST = false;
-
- // System services / libraries we use.
- private final Context mContext;
- private final Random mRandom;
- private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
-
- // Sockets.
- // - We use a packet socket to receive, because servers send us packets bound for IP addresses
- // which we have not yet configured, and the kernel protocol stack drops these.
- // - We use a UDP socket to send, so the kernel handles ARP and routing for us (DHCP servers can
- // be off-link as well as on-link).
- private FileDescriptor mPacketSock;
- private FileDescriptor mUdpSock;
- private ReceiveThread mReceiveThread;
-
- // State variables.
- private final StateMachine mController;
- private final WakeupMessage mKickAlarm;
- private final WakeupMessage mTimeoutAlarm;
- private final WakeupMessage mRenewAlarm;
- private final WakeupMessage mRebindAlarm;
- private final WakeupMessage mExpiryAlarm;
- private final String mIfaceName;
-
- private boolean mRegisteredForPreDhcpNotification;
- private InterfaceParams mIface;
- // TODO: MacAddress-ify more of this class hierarchy.
- private byte[] mHwAddr;
- private SocketAddress mInterfaceBroadcastAddr;
- private int mTransactionId;
- private long mTransactionStartMillis;
- private DhcpResults mDhcpLease;
- private long mDhcpLeaseExpiry;
- private DhcpResults mOffer;
-
- // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState.
- private long mLastInitEnterTime;
- private long mLastBoundExitTime;
-
- // States.
- private State mStoppedState = new StoppedState();
- private State mDhcpState = new DhcpState();
- private State mDhcpInitState = new DhcpInitState();
- private State mDhcpSelectingState = new DhcpSelectingState();
- private State mDhcpRequestingState = new DhcpRequestingState();
- private State mDhcpHaveLeaseState = new DhcpHaveLeaseState();
- private State mConfiguringInterfaceState = new ConfiguringInterfaceState();
- private State mDhcpBoundState = new DhcpBoundState();
- private State mDhcpRenewingState = new DhcpRenewingState();
- private State mDhcpRebindingState = new DhcpRebindingState();
- private State mDhcpInitRebootState = new DhcpInitRebootState();
- private State mDhcpRebootingState = new DhcpRebootingState();
- private State mWaitBeforeStartState = new WaitBeforeStartState(mDhcpInitState);
- private State mWaitBeforeRenewalState = new WaitBeforeRenewalState(mDhcpRenewingState);
-
- private WakeupMessage makeWakeupMessage(String cmdName, int cmd) {
- cmdName = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName;
- return new WakeupMessage(mContext, getHandler(), cmdName, cmd);
- }
-
- // TODO: Take an InterfaceParams instance instead of an interface name String.
- private DhcpClient(Context context, StateMachine controller, String iface) {
- super(TAG, controller.getHandler());
-
- mContext = context;
- mController = controller;
- mIfaceName = iface;
-
- addState(mStoppedState);
- addState(mDhcpState);
- addState(mDhcpInitState, mDhcpState);
- addState(mWaitBeforeStartState, mDhcpState);
- addState(mDhcpSelectingState, mDhcpState);
- addState(mDhcpRequestingState, mDhcpState);
- addState(mDhcpHaveLeaseState, mDhcpState);
- addState(mConfiguringInterfaceState, mDhcpHaveLeaseState);
- addState(mDhcpBoundState, mDhcpHaveLeaseState);
- addState(mWaitBeforeRenewalState, mDhcpHaveLeaseState);
- addState(mDhcpRenewingState, mDhcpHaveLeaseState);
- addState(mDhcpRebindingState, mDhcpHaveLeaseState);
- addState(mDhcpInitRebootState, mDhcpState);
- addState(mDhcpRebootingState, mDhcpState);
-
- setInitialState(mStoppedState);
-
- mRandom = new Random();
-
- // Used to schedule packet retransmissions.
- mKickAlarm = makeWakeupMessage("KICK", CMD_KICK);
- // Used to time out PacketRetransmittingStates.
- mTimeoutAlarm = makeWakeupMessage("TIMEOUT", CMD_TIMEOUT);
- // Used to schedule DHCP reacquisition.
- mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP);
- mRebindAlarm = makeWakeupMessage("REBIND", CMD_REBIND_DHCP);
- mExpiryAlarm = makeWakeupMessage("EXPIRY", CMD_EXPIRE_DHCP);
- }
-
- public void registerForPreDhcpNotification() {
- mRegisteredForPreDhcpNotification = true;
- }
-
- public static DhcpClient makeDhcpClient(
- Context context, StateMachine controller, InterfaceParams ifParams) {
- DhcpClient client = new DhcpClient(context, controller, ifParams.name);
- client.mIface = ifParams;
- client.start();
- return client;
- }
-
- private boolean initInterface() {
- if (mIface == null) mIface = InterfaceParams.getByName(mIfaceName);
- if (mIface == null) {
- Log.e(TAG, "Can't determine InterfaceParams for " + mIfaceName);
- return false;
- }
-
- mHwAddr = mIface.macAddr.toByteArray();
- mInterfaceBroadcastAddr = makePacketSocketAddress(mIface.index, DhcpPacket.ETHER_BROADCAST);
- return true;
- }
-
- private void startNewTransaction() {
- mTransactionId = mRandom.nextInt();
- mTransactionStartMillis = SystemClock.elapsedRealtime();
- }
-
- private boolean initSockets() {
- return initPacketSocket() && initUdpSocket();
- }
-
- private boolean initPacketSocket() {
- try {
- mPacketSock = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IP);
- SocketAddress addr = makePacketSocketAddress((short) ETH_P_IP, mIface.index);
- Os.bind(mPacketSock, addr);
- NetworkStackUtils.attachDhcpFilter(mPacketSock);
- } catch(SocketException|ErrnoException e) {
- Log.e(TAG, "Error creating packet socket", e);
- return false;
- }
- return true;
- }
-
- private boolean initUdpSocket() {
- final int oldTag = TrafficStats.getAndSetThreadStatsTag(
- TrafficStatsConstants.TAG_SYSTEM_DHCP);
- try {
- mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- SocketUtils.bindSocketToInterface(mUdpSock, mIfaceName);
- Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
- Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1);
- Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_RCVBUF, 0);
- Os.bind(mUdpSock, IPV4_ADDR_ANY, DhcpPacket.DHCP_CLIENT);
- } catch(SocketException|ErrnoException e) {
- Log.e(TAG, "Error creating UDP socket", e);
- return false;
- } finally {
- TrafficStats.setThreadStatsTag(oldTag);
- }
- return true;
- }
-
- private boolean connectUdpSock(Inet4Address to) {
- try {
- Os.connect(mUdpSock, to, DhcpPacket.DHCP_SERVER);
- return true;
- } catch (SocketException|ErrnoException e) {
- Log.e(TAG, "Error connecting UDP socket", e);
- return false;
- }
- }
-
- private void closeSockets() {
- closeSocketQuietly(mUdpSock);
- closeSocketQuietly(mPacketSock);
- }
-
- class ReceiveThread extends Thread {
-
- private final byte[] mPacket = new byte[DhcpPacket.MAX_LENGTH];
- private volatile boolean mStopped = false;
-
- public void halt() {
- mStopped = true;
- closeSockets(); // Interrupts the read() call the thread is blocked in.
- }
-
- @Override
- public void run() {
- if (DBG) Log.d(TAG, "Receive thread started");
- while (!mStopped) {
- int length = 0; // Or compiler can't tell it's initialized if a parse error occurs.
- try {
- length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
- DhcpPacket packet = null;
- packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2);
- if (DBG) Log.d(TAG, "Received packet: " + packet);
- sendMessage(CMD_RECEIVED_PACKET, packet);
- } catch (IOException|ErrnoException e) {
- if (!mStopped) {
- Log.e(TAG, "Read error", e);
- logError(DhcpErrorEvent.RECEIVE_ERROR);
- }
- } catch (DhcpPacket.ParseException e) {
- Log.e(TAG, "Can't parse packet: " + e.getMessage());
- if (PACKET_DBG) {
- Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length));
- }
- if (e.errorCode == DhcpErrorEvent.DHCP_NO_COOKIE) {
- int snetTagId = 0x534e4554;
- String bugId = "31850211";
- int uid = -1;
- String data = DhcpPacket.ParseException.class.getName();
- EventLog.writeEvent(snetTagId, bugId, uid, data);
- }
- logError(e.errorCode);
- }
- }
- if (DBG) Log.d(TAG, "Receive thread stopped");
- }
- }
-
- private short getSecs() {
- return (short) ((SystemClock.elapsedRealtime() - mTransactionStartMillis) / 1000);
- }
-
- private boolean transmitPacket(ByteBuffer buf, String description, int encap, Inet4Address to) {
- try {
- if (encap == DhcpPacket.ENCAP_L2) {
- if (DBG) Log.d(TAG, "Broadcasting " + description);
- Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr);
- } else if (encap == DhcpPacket.ENCAP_BOOTP && to.equals(INADDR_BROADCAST)) {
- if (DBG) Log.d(TAG, "Broadcasting " + description);
- // We only send L3-encapped broadcasts in DhcpRebindingState,
- // where we have an IP address and an unconnected UDP socket.
- //
- // N.B.: We only need this codepath because DhcpRequestPacket
- // hardcodes the source IP address to 0.0.0.0. We could reuse
- // the packet socket if this ever changes.
- Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER);
- } else {
- // It's safe to call getpeername here, because we only send unicast packets if we
- // have an IP address, and we connect the UDP socket in DhcpBoundState#enter.
- if (DBG) Log.d(TAG, String.format("Unicasting %s to %s",
- description, Os.getpeername(mUdpSock)));
- Os.write(mUdpSock, buf);
- }
- } catch(ErrnoException|IOException e) {
- Log.e(TAG, "Can't send packet: ", e);
- return false;
- }
- return true;
- }
-
- private boolean sendDiscoverPacket() {
- ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
- DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
- DO_UNICAST, REQUESTED_PARAMS);
- return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST);
- }
-
- private boolean sendRequestPacket(
- Inet4Address clientAddress, Inet4Address requestedAddress,
- Inet4Address serverAddress, Inet4Address to) {
- // TODO: should we use the transaction ID from the server?
- final int encap = INADDR_ANY.equals(clientAddress)
- ? DhcpPacket.ENCAP_L2 : DhcpPacket.ENCAP_BOOTP;
-
- ByteBuffer packet = DhcpPacket.buildRequestPacket(
- encap, mTransactionId, getSecs(), clientAddress,
- DO_UNICAST, mHwAddr, requestedAddress,
- serverAddress, REQUESTED_PARAMS, null);
- String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null;
- String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
- " request=" + requestedAddress.getHostAddress() +
- " serverid=" + serverStr;
- return transmitPacket(packet, description, encap, to);
- }
-
- private void scheduleLeaseTimers() {
- if (mDhcpLeaseExpiry == 0) {
- Log.d(TAG, "Infinite lease, no timer scheduling needed");
- return;
- }
-
- final long now = SystemClock.elapsedRealtime();
-
- // TODO: consider getting the renew and rebind timers from T1 and T2.
- // See also:
- // https://tools.ietf.org/html/rfc2131#section-4.4.5
- // https://tools.ietf.org/html/rfc1533#section-9.9
- // https://tools.ietf.org/html/rfc1533#section-9.10
- final long remainingDelay = mDhcpLeaseExpiry - now;
- final long renewDelay = remainingDelay / 2;
- final long rebindDelay = remainingDelay * 7 / 8;
- mRenewAlarm.schedule(now + renewDelay);
- mRebindAlarm.schedule(now + rebindDelay);
- mExpiryAlarm.schedule(now + remainingDelay);
- Log.d(TAG, "Scheduling renewal in " + (renewDelay / 1000) + "s");
- Log.d(TAG, "Scheduling rebind in " + (rebindDelay / 1000) + "s");
- Log.d(TAG, "Scheduling expiry in " + (remainingDelay / 1000) + "s");
- }
-
- private void notifySuccess() {
- mController.sendMessage(
- CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
- }
-
- private void notifyFailure() {
- mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0, null);
- }
-
- private void acceptDhcpResults(DhcpResults results, String msg) {
- mDhcpLease = results;
- if (mDhcpLease.dnsServers.isEmpty()) {
- // supplement customized dns servers
- String[] dnsServersList =
- mContext.getResources().getStringArray(R.array.config_default_dns_servers);
- for (final String dnsServer : dnsServersList) {
- try {
- mDhcpLease.dnsServers.add(InetAddresses.parseNumericAddress(dnsServer));
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Invalid default DNS server: " + dnsServer, e);
- }
- }
- }
- mOffer = null;
- Log.d(TAG, msg + " lease: " + mDhcpLease);
- notifySuccess();
- }
-
- private void clearDhcpState() {
- mDhcpLease = null;
- mDhcpLeaseExpiry = 0;
- mOffer = null;
- }
-
- /**
- * Quit the DhcpStateMachine.
- *
- * @hide
- */
- public void doQuit() {
- Log.d(TAG, "doQuit");
- quit();
- }
-
- @Override
- protected void onQuitting() {
- Log.d(TAG, "onQuitting");
- mController.sendMessage(CMD_ON_QUIT);
- }
-
- abstract class LoggingState extends State {
- private long mEnterTimeMs;
-
- @Override
- public void enter() {
- if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
- mEnterTimeMs = SystemClock.elapsedRealtime();
- }
-
- @Override
- public void exit() {
- long durationMs = SystemClock.elapsedRealtime() - mEnterTimeMs;
- logState(getName(), (int) durationMs);
- }
-
- private String messageName(int what) {
- return sMessageNames.get(what, Integer.toString(what));
- }
-
- private String messageToString(Message message) {
- long now = SystemClock.uptimeMillis();
- return new StringBuilder(" ")
- .append(message.getWhen() - now)
- .append(messageName(message.what))
- .append(" ").append(message.arg1)
- .append(" ").append(message.arg2)
- .append(" ").append(message.obj)
- .toString();
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (MSG_DBG) {
- Log.d(TAG, getName() + messageToString(message));
- }
- return NOT_HANDLED;
- }
-
- @Override
- public String getName() {
- // All DhcpClient's states are inner classes with a well defined name.
- // Use getSimpleName() and avoid super's getName() creating new String instances.
- return getClass().getSimpleName();
- }
- }
-
- // Sends CMD_PRE_DHCP_ACTION to the controller, waits for the controller to respond with
- // CMD_PRE_DHCP_ACTION_COMPLETE, and then transitions to mOtherState.
- abstract class WaitBeforeOtherState extends LoggingState {
- protected State mOtherState;
-
- @Override
- public void enter() {
- super.enter();
- mController.sendMessage(CMD_PRE_DHCP_ACTION);
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case CMD_PRE_DHCP_ACTION_COMPLETE:
- transitionTo(mOtherState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
- }
-
- class StoppedState extends State {
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_START_DHCP:
- if (mRegisteredForPreDhcpNotification) {
- transitionTo(mWaitBeforeStartState);
- } else {
- transitionTo(mDhcpInitState);
- }
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
- }
-
- class WaitBeforeStartState extends WaitBeforeOtherState {
- public WaitBeforeStartState(State otherState) {
- super();
- mOtherState = otherState;
- }
- }
-
- class WaitBeforeRenewalState extends WaitBeforeOtherState {
- public WaitBeforeRenewalState(State otherState) {
- super();
- mOtherState = otherState;
- }
- }
-
- class DhcpState extends State {
- @Override
- public void enter() {
- clearDhcpState();
- if (initInterface() && initSockets()) {
- mReceiveThread = new ReceiveThread();
- mReceiveThread.start();
- } else {
- notifyFailure();
- transitionTo(mStoppedState);
- }
- }
-
- @Override
- public void exit() {
- if (mReceiveThread != null) {
- mReceiveThread.halt(); // Also closes sockets.
- mReceiveThread = null;
- }
- clearDhcpState();
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case CMD_STOP_DHCP:
- transitionTo(mStoppedState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
- }
-
- public boolean isValidPacket(DhcpPacket packet) {
- // TODO: check checksum.
- int xid = packet.getTransactionId();
- if (xid != mTransactionId) {
- Log.d(TAG, "Unexpected transaction ID " + xid + ", expected " + mTransactionId);
- return false;
- }
- if (!Arrays.equals(packet.getClientMac(), mHwAddr)) {
- Log.d(TAG, "MAC addr mismatch: got " +
- HexDump.toHexString(packet.getClientMac()) + ", expected " +
- HexDump.toHexString(packet.getClientMac()));
- return false;
- }
- return true;
- }
-
- public void setDhcpLeaseExpiry(DhcpPacket packet) {
- long leaseTimeMillis = packet.getLeaseTimeMillis();
- mDhcpLeaseExpiry =
- (leaseTimeMillis > 0) ? SystemClock.elapsedRealtime() + leaseTimeMillis : 0;
- }
-
- /**
- * Retransmits packets using jittered exponential backoff with an optional timeout. Packet
- * transmission is triggered by CMD_KICK, which is sent by an AlarmManager alarm. If a subclass
- * sets mTimeout to a positive value, then timeout() is called by an AlarmManager alarm mTimeout
- * milliseconds after entering the state. Kicks and timeouts are cancelled when leaving the
- * state.
- *
- * Concrete subclasses must implement sendPacket, which is called when the alarm fires and a
- * packet needs to be transmitted, and receivePacket, which is triggered by CMD_RECEIVED_PACKET
- * sent by the receive thread. They may also set mTimeout and implement timeout.
- */
- abstract class PacketRetransmittingState extends LoggingState {
-
- private int mTimer;
- protected int mTimeout = 0;
-
- @Override
- public void enter() {
- super.enter();
- initTimer();
- maybeInitTimeout();
- sendMessage(CMD_KICK);
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case CMD_KICK:
- sendPacket();
- scheduleKick();
- return HANDLED;
- case CMD_RECEIVED_PACKET:
- receivePacket((DhcpPacket) message.obj);
- return HANDLED;
- case CMD_TIMEOUT:
- timeout();
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- @Override
- public void exit() {
- super.exit();
- mKickAlarm.cancel();
- mTimeoutAlarm.cancel();
- }
-
- abstract protected boolean sendPacket();
- abstract protected void receivePacket(DhcpPacket packet);
- protected void timeout() {}
-
- protected void initTimer() {
- mTimer = FIRST_TIMEOUT_MS;
- }
-
- protected int jitterTimer(int baseTimer) {
- int maxJitter = baseTimer / 10;
- int jitter = mRandom.nextInt(2 * maxJitter) - maxJitter;
- return baseTimer + jitter;
- }
-
- protected void scheduleKick() {
- long now = SystemClock.elapsedRealtime();
- long timeout = jitterTimer(mTimer);
- long alarmTime = now + timeout;
- mKickAlarm.schedule(alarmTime);
- mTimer *= 2;
- if (mTimer > MAX_TIMEOUT_MS) {
- mTimer = MAX_TIMEOUT_MS;
- }
- }
-
- protected void maybeInitTimeout() {
- if (mTimeout > 0) {
- long alarmTime = SystemClock.elapsedRealtime() + mTimeout;
- mTimeoutAlarm.schedule(alarmTime);
- }
- }
- }
-
- class DhcpInitState extends PacketRetransmittingState {
- public DhcpInitState() {
- super();
- }
-
- @Override
- public void enter() {
- super.enter();
- startNewTransaction();
- mLastInitEnterTime = SystemClock.elapsedRealtime();
- }
-
- protected boolean sendPacket() {
- return sendDiscoverPacket();
- }
-
- protected void receivePacket(DhcpPacket packet) {
- if (!isValidPacket(packet)) return;
- if (!(packet instanceof DhcpOfferPacket)) return;
- mOffer = packet.toDhcpResults();
- if (mOffer != null) {
- Log.d(TAG, "Got pending lease: " + mOffer);
- transitionTo(mDhcpRequestingState);
- }
- }
- }
-
- // Not implemented. We request the first offer we receive.
- class DhcpSelectingState extends LoggingState {
- }
-
- class DhcpRequestingState extends PacketRetransmittingState {
- public DhcpRequestingState() {
- mTimeout = DHCP_TIMEOUT_MS / 2;
- }
-
- protected boolean sendPacket() {
- return sendRequestPacket(
- INADDR_ANY, // ciaddr
- (Inet4Address) mOffer.ipAddress.getAddress(), // DHCP_REQUESTED_IP
- (Inet4Address) mOffer.serverAddress, // DHCP_SERVER_IDENTIFIER
- INADDR_BROADCAST); // packet destination address
- }
-
- protected void receivePacket(DhcpPacket packet) {
- if (!isValidPacket(packet)) return;
- if ((packet instanceof DhcpAckPacket)) {
- DhcpResults results = packet.toDhcpResults();
- if (results != null) {
- setDhcpLeaseExpiry(packet);
- acceptDhcpResults(results, "Confirmed");
- transitionTo(mConfiguringInterfaceState);
- }
- } else if (packet instanceof DhcpNakPacket) {
- // TODO: Wait a while before returning into INIT state.
- Log.d(TAG, "Received NAK, returning to INIT");
- mOffer = null;
- transitionTo(mDhcpInitState);
- }
- }
-
- @Override
- protected void timeout() {
- // After sending REQUESTs unsuccessfully for a while, go back to init.
- transitionTo(mDhcpInitState);
- }
- }
-
- class DhcpHaveLeaseState extends State {
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_EXPIRE_DHCP:
- Log.d(TAG, "Lease expired!");
- notifyFailure();
- transitionTo(mDhcpInitState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- @Override
- public void exit() {
- // Clear any extant alarms.
- mRenewAlarm.cancel();
- mRebindAlarm.cancel();
- mExpiryAlarm.cancel();
- clearDhcpState();
- // Tell IpManager to clear the IPv4 address. There is no need to
- // wait for confirmation since any subsequent packets are sent from
- // INADDR_ANY anyway (DISCOVER, REQUEST).
- mController.sendMessage(CMD_CLEAR_LINKADDRESS);
- }
- }
-
- class ConfiguringInterfaceState extends LoggingState {
- @Override
- public void enter() {
- super.enter();
- mController.sendMessage(CMD_CONFIGURE_LINKADDRESS, mDhcpLease.ipAddress);
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case EVENT_LINKADDRESS_CONFIGURED:
- transitionTo(mDhcpBoundState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
- }
-
- class DhcpBoundState extends LoggingState {
- @Override
- public void enter() {
- super.enter();
- if (mDhcpLease.serverAddress != null && !connectUdpSock(mDhcpLease.serverAddress)) {
- // There's likely no point in going into DhcpInitState here, we'll probably
- // just repeat the transaction, get the same IP address as before, and fail.
- //
- // NOTE: It is observed that connectUdpSock() basically never fails, due to
- // SO_BINDTODEVICE. Examining the local socket address shows it will happily
- // return an IPv4 address from another interface, or even return "0.0.0.0".
- //
- // TODO: Consider deleting this check, following testing on several kernels.
- notifyFailure();
- transitionTo(mStoppedState);
- }
-
- scheduleLeaseTimers();
- logTimeToBoundState();
- }
-
- @Override
- public void exit() {
- super.exit();
- mLastBoundExitTime = SystemClock.elapsedRealtime();
- }
-
- @Override
- public boolean processMessage(Message message) {
- super.processMessage(message);
- switch (message.what) {
- case CMD_RENEW_DHCP:
- if (mRegisteredForPreDhcpNotification) {
- transitionTo(mWaitBeforeRenewalState);
- } else {
- transitionTo(mDhcpRenewingState);
- }
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- private void logTimeToBoundState() {
- long now = SystemClock.elapsedRealtime();
- if (mLastBoundExitTime > mLastInitEnterTime) {
- logState(EVENT_RENEWING_BOUND, (int) (now - mLastBoundExitTime));
- } else {
- logState(EVENT_INITIAL_BOUND, (int) (now - mLastInitEnterTime));
- }
- }
- }
-
- abstract class DhcpReacquiringState extends PacketRetransmittingState {
- protected String mLeaseMsg;
-
- @Override
- public void enter() {
- super.enter();
- startNewTransaction();
- }
-
- abstract protected Inet4Address packetDestination();
-
- protected boolean sendPacket() {
- return sendRequestPacket(
- (Inet4Address) mDhcpLease.ipAddress.getAddress(), // ciaddr
- INADDR_ANY, // DHCP_REQUESTED_IP
- null, // DHCP_SERVER_IDENTIFIER
- packetDestination()); // packet destination address
- }
-
- protected void receivePacket(DhcpPacket packet) {
- if (!isValidPacket(packet)) return;
- if ((packet instanceof DhcpAckPacket)) {
- final DhcpResults results = packet.toDhcpResults();
- if (results != null) {
- if (!mDhcpLease.ipAddress.equals(results.ipAddress)) {
- Log.d(TAG, "Renewed lease not for our current IP address!");
- notifyFailure();
- transitionTo(mDhcpInitState);
- }
- setDhcpLeaseExpiry(packet);
- // Updating our notion of DhcpResults here only causes the
- // DNS servers and routes to be updated in LinkProperties
- // in IpManager and by any overridden relevant handlers of
- // the registered IpManager.Callback. IP address changes
- // are not supported here.
- acceptDhcpResults(results, mLeaseMsg);
- transitionTo(mDhcpBoundState);
- }
- } else if (packet instanceof DhcpNakPacket) {
- Log.d(TAG, "Received NAK, returning to INIT");
- notifyFailure();
- transitionTo(mDhcpInitState);
- }
- }
- }
-
- class DhcpRenewingState extends DhcpReacquiringState {
- public DhcpRenewingState() {
- mLeaseMsg = "Renewed";
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (super.processMessage(message) == HANDLED) {
- return HANDLED;
- }
-
- switch (message.what) {
- case CMD_REBIND_DHCP:
- transitionTo(mDhcpRebindingState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- @Override
- protected Inet4Address packetDestination() {
- // Not specifying a SERVER_IDENTIFIER option is a violation of RFC 2131, but...
- // http://b/25343517 . Try to make things work anyway by using broadcast renews.
- return (mDhcpLease.serverAddress != null) ?
- mDhcpLease.serverAddress : INADDR_BROADCAST;
- }
- }
-
- class DhcpRebindingState extends DhcpReacquiringState {
- public DhcpRebindingState() {
- mLeaseMsg = "Rebound";
- }
-
- @Override
- public void enter() {
- super.enter();
-
- // We need to broadcast and possibly reconnect the socket to a
- // completely different server.
- closeSocketQuietly(mUdpSock);
- if (!initUdpSocket()) {
- Log.e(TAG, "Failed to recreate UDP socket");
- transitionTo(mDhcpInitState);
- }
- }
-
- @Override
- protected Inet4Address packetDestination() {
- return INADDR_BROADCAST;
- }
- }
-
- class DhcpInitRebootState extends LoggingState {
- }
-
- class DhcpRebootingState extends LoggingState {
- }
-
- private void logError(int errorCode) {
- mMetricsLog.log(mIfaceName, new DhcpErrorEvent(errorCode));
- }
-
- private void logState(String name, int durationMs) {
- final DhcpClientEvent event = new DhcpClientEvent.Builder()
- .setMsg(name)
- .setDurationMs(durationMs)
- .build();
- mMetricsLog.log(mIfaceName, event);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpDeclinePacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpDeclinePacket.java
deleted file mode 100644
index 7ecdea7..0000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpDeclinePacket.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-DECLINE packet.
- */
-class DhcpDeclinePacket extends DhcpPacket {
- /**
- * Generates a DECLINE packet with the specified parameters.
- */
- DhcpDeclinePacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
- Inet4Address nextIp, Inet4Address relayIp,
- byte[] clientMac) {
- super(transId, secs, clientIp, yourIp, nextIp, relayIp, clientMac, false);
- }
-
- public String toString() {
- String s = super.toString();
- return s + " DECLINE";
- }
-
- /**
- * Fills in a packet with the requested DECLINE attributes.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
-
- fillInPacket(encap, mClientIp, mYourIp, destUdp, srcUdp, result,
- DHCP_BOOTREQUEST, false);
- result.flip();
- return result;
- }
-
- /**
- * Adds optional parameters to the DECLINE packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_DECLINE);
- addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId());
- // RFC 2131 says we MUST NOT include our common client TLVs or the parameter request list.
- addTlvEnd(buffer);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpDiscoverPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpDiscoverPacket.java
deleted file mode 100644
index 11f2b61..0000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpDiscoverPacket.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-DISCOVER packet.
- */
-class DhcpDiscoverPacket extends DhcpPacket {
- /**
- * The IP address of the client which sent this packet.
- */
- final Inet4Address mSrcIp;
-
- /**
- * Generates a DISCOVER packet with the specified parameters.
- */
- DhcpDiscoverPacket(int transId, short secs, Inet4Address relayIp, byte[] clientMac,
- boolean broadcast, Inet4Address srcIp) {
- super(transId, secs, INADDR_ANY, INADDR_ANY, INADDR_ANY, relayIp, clientMac, broadcast);
- mSrcIp = srcIp;
- }
-
- public String toString() {
- String s = super.toString();
- return s + " DISCOVER " +
- (mBroadcast ? "broadcast " : "unicast ");
- }
-
- /**
- * Fills in a packet with the requested DISCOVER parameters.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- fillInPacket(encap, INADDR_BROADCAST, mSrcIp, destUdp, srcUdp, result, DHCP_BOOTREQUEST,
- mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds optional parameters to a DISCOVER packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_DISCOVER);
- addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId());
- addCommonClientTlvs(buffer);
- addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
- addTlvEnd(buffer);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpInformPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpInformPacket.java
deleted file mode 100644
index 7a83466..0000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpInformPacket.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the (unused) DHCP-INFORM packet.
- */
-class DhcpInformPacket extends DhcpPacket {
- /**
- * Generates an INFORM packet with the specified parameters.
- */
- DhcpInformPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
- Inet4Address nextIp, Inet4Address relayIp,
- byte[] clientMac) {
- super(transId, secs, clientIp, yourIp, nextIp, relayIp, clientMac, false);
- }
-
- public String toString() {
- String s = super.toString();
- return s + " INFORM";
- }
-
- /**
- * Builds an INFORM packet.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
-
- fillInPacket(encap, mClientIp, mYourIp, destUdp, srcUdp, result,
- DHCP_BOOTREQUEST, false);
- result.flip();
- return result;
- }
-
- /**
- * Adds additional parameters to the INFORM packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_INFORM);
- addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId());
- addCommonClientTlvs(buffer);
- addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
- addTlvEnd(buffer);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpLease.java b/packages/NetworkStack/src/android/net/dhcp/DhcpLease.java
deleted file mode 100644
index 6849cfa..0000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpLease.java
+++ /dev/null
@@ -1,153 +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 android.net.dhcp;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.MacAddress;
-import android.os.SystemClock;
-import android.text.TextUtils;
-
-import com.android.internal.util.HexDump;
-
-import java.net.Inet4Address;
-import java.util.Arrays;
-import java.util.Objects;
-
-/**
- * An IPv4 address assignment done through DHCPv4.
- * @hide
- */
-public class DhcpLease {
- public static final long EXPIRATION_NEVER = Long.MAX_VALUE;
- public static final String HOSTNAME_NONE = null;
-
- @Nullable
- private final byte[] mClientId;
- @NonNull
- private final MacAddress mHwAddr;
- @NonNull
- private final Inet4Address mNetAddr;
- /**
- * Expiration time for the lease, to compare with {@link SystemClock#elapsedRealtime()}.
- */
- private final long mExpTime;
- @Nullable
- private final String mHostname;
-
- public DhcpLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr,
- @NonNull Inet4Address netAddr, long expTime, @Nullable String hostname) {
- mClientId = (clientId == null ? null : Arrays.copyOf(clientId, clientId.length));
- mHwAddr = hwAddr;
- mNetAddr = netAddr;
- mExpTime = expTime;
- mHostname = hostname;
- }
-
- /**
- * Get the clientId associated with this lease, if any.
- *
- * <p>If the lease is not associated to a clientId, this returns null.
- */
- @Nullable
- public byte[] getClientId() {
- if (mClientId == null) {
- return null;
- }
- return Arrays.copyOf(mClientId, mClientId.length);
- }
-
- @NonNull
- public MacAddress getHwAddr() {
- return mHwAddr;
- }
-
- @Nullable
- public String getHostname() {
- return mHostname;
- }
-
- @NonNull
- public Inet4Address getNetAddr() {
- return mNetAddr;
- }
-
- public long getExpTime() {
- return mExpTime;
- }
-
- /**
- * Push back the expiration time of this lease. If the provided time is sooner than the original
- * expiration time, the lease time will not be updated.
- *
- * <p>The lease hostname is updated with the provided one if set.
- * @return A {@link DhcpLease} with expiration time set to max(expTime, currentExpTime)
- */
- public DhcpLease renewedLease(long expTime, @Nullable String hostname) {
- return new DhcpLease(mClientId, mHwAddr, mNetAddr, Math.max(expTime, mExpTime),
- (hostname == null ? mHostname : hostname));
- }
-
- /**
- * Determine whether this lease matches a client with the specified parameters.
- * @param clientId clientId of the client if any, or null otherwise.
- * @param hwAddr Hardware address of the client.
- */
- public boolean matchesClient(@Nullable byte[] clientId, @NonNull MacAddress hwAddr) {
- if (mClientId != null) {
- return Arrays.equals(mClientId, clientId);
- } else {
- return clientId == null && mHwAddr.equals(hwAddr);
- }
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof DhcpLease)) {
- return false;
- }
- final DhcpLease other = (DhcpLease) obj;
- return Arrays.equals(mClientId, other.mClientId)
- && mHwAddr.equals(other.mHwAddr)
- && mNetAddr.equals(other.mNetAddr)
- && mExpTime == other.mExpTime
- && TextUtils.equals(mHostname, other.mHostname);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mClientId, mHwAddr, mNetAddr, mHostname, mExpTime);
- }
-
- static String clientIdToString(byte[] bytes) {
- if (bytes == null) {
- return "null";
- }
- return HexDump.toHexString(bytes);
- }
-
- static String inet4AddrToString(@Nullable Inet4Address addr) {
- return (addr == null) ? "null" : addr.getHostAddress();
- }
-
- @Override
- public String toString() {
- return String.format("clientId: %s, hwAddr: %s, netAddr: %s, expTime: %d, hostname: %s",
- clientIdToString(mClientId), mHwAddr.toString(), inet4AddrToString(mNetAddr),
- mExpTime, mHostname);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java b/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java
deleted file mode 100644
index 0a15cd7..0000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java
+++ /dev/null
@@ -1,546 +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 android.net.dhcp;
-
-import static android.net.dhcp.DhcpLease.EXPIRATION_NEVER;
-import static android.net.dhcp.DhcpLease.inet4AddrToString;
-import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH;
-import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
-import static android.net.shared.Inet4AddressUtils.prefixLengthToV4NetmaskIntHTH;
-
-import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
-import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_BITS;
-
-import static java.lang.Math.min;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.IpPrefix;
-import android.net.MacAddress;
-import android.net.dhcp.DhcpServer.Clock;
-import android.net.util.SharedLog;
-import android.util.ArrayMap;
-
-import java.net.Inet4Address;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.function.Function;
-
-/**
- * A repository managing IPv4 address assignments through DHCPv4.
- *
- * <p>This class is not thread-safe. All public methods should be called on a common thread or
- * use some synchronization mechanism.
- *
- * <p>Methods are optimized for a small number of allocated leases, assuming that most of the time
- * only 2~10 addresses will be allocated, which is the common case. Managing a large number of
- * addresses is supported but will be slower: some operations have complexity in O(num_leases).
- * @hide
- */
-class DhcpLeaseRepository {
- public static final byte[] CLIENTID_UNSPEC = null;
- public static final Inet4Address INETADDR_UNSPEC = null;
-
- @NonNull
- private final SharedLog mLog;
- @NonNull
- private final Clock mClock;
-
- @NonNull
- private IpPrefix mPrefix;
- @NonNull
- private Set<Inet4Address> mReservedAddrs;
- private int mSubnetAddr;
- private int mSubnetMask;
- private int mNumAddresses;
- private long mLeaseTimeMs;
-
- /**
- * Next timestamp when committed or declined leases should be checked for expired ones. This
- * will always be lower than or equal to the time for the first lease to expire: it's OK not to
- * update this when removing entries, but it must always be updated when adding/updating.
- */
- private long mNextExpirationCheck = EXPIRATION_NEVER;
-
- static class DhcpLeaseException extends Exception {
- DhcpLeaseException(String message) {
- super(message);
- }
- }
-
- static class OutOfAddressesException extends DhcpLeaseException {
- OutOfAddressesException(String message) {
- super(message);
- }
- }
-
- static class InvalidAddressException extends DhcpLeaseException {
- InvalidAddressException(String message) {
- super(message);
- }
- }
-
- static class InvalidSubnetException extends DhcpLeaseException {
- InvalidSubnetException(String message) {
- super(message);
- }
- }
-
- /**
- * Leases by IP address
- */
- private final ArrayMap<Inet4Address, DhcpLease> mCommittedLeases = new ArrayMap<>();
-
- /**
- * Map address -> expiration timestamp in ms. Addresses are guaranteed to be valid as defined
- * by {@link #isValidAddress(Inet4Address)}, but are not necessarily otherwise available for
- * assignment.
- */
- private final LinkedHashMap<Inet4Address, Long> mDeclinedAddrs = new LinkedHashMap<>();
-
- DhcpLeaseRepository(@NonNull IpPrefix prefix, @NonNull Set<Inet4Address> reservedAddrs,
- long leaseTimeMs, @NonNull SharedLog log, @NonNull Clock clock) {
- updateParams(prefix, reservedAddrs, leaseTimeMs);
- mLog = log;
- mClock = clock;
- }
-
- public void updateParams(@NonNull IpPrefix prefix, @NonNull Set<Inet4Address> reservedAddrs,
- long leaseTimeMs) {
- mPrefix = prefix;
- mReservedAddrs = Collections.unmodifiableSet(new HashSet<>(reservedAddrs));
- mSubnetMask = prefixLengthToV4NetmaskIntHTH(prefix.getPrefixLength());
- mSubnetAddr = inet4AddressToIntHTH((Inet4Address) prefix.getAddress()) & mSubnetMask;
- mNumAddresses = 1 << (IPV4_ADDR_BITS - prefix.getPrefixLength());
- mLeaseTimeMs = leaseTimeMs;
-
- cleanMap(mCommittedLeases);
- cleanMap(mDeclinedAddrs);
- }
-
- /**
- * From a map keyed by {@link Inet4Address}, remove entries where the key is invalid (as
- * specified by {@link #isValidAddress(Inet4Address)}), or is a reserved address.
- */
- private <T> void cleanMap(Map<Inet4Address, T> map) {
- final Iterator<Entry<Inet4Address, T>> it = map.entrySet().iterator();
- while (it.hasNext()) {
- final Inet4Address addr = it.next().getKey();
- if (!isValidAddress(addr) || mReservedAddrs.contains(addr)) {
- it.remove();
- }
- }
- }
-
- /**
- * Get a DHCP offer, to reply to a DHCPDISCOVER. Follows RFC2131 #4.3.1.
- *
- * @param clientId Client identifier option if specified, or {@link #CLIENTID_UNSPEC}
- * @param relayAddr Internet address of the relay (giaddr), can be {@link Inet4Address#ANY}
- * @param reqAddr Requested address by the client (option 50), or {@link #INETADDR_UNSPEC}
- * @param hostname Client-provided hostname, or {@link DhcpLease#HOSTNAME_NONE}
- * @throws OutOfAddressesException The server does not have any available address
- * @throws InvalidSubnetException The lease was requested from an unsupported subnet
- */
- @NonNull
- public DhcpLease getOffer(@Nullable byte[] clientId, @NonNull MacAddress hwAddr,
- @NonNull Inet4Address relayAddr, @Nullable Inet4Address reqAddr,
- @Nullable String hostname) throws OutOfAddressesException, InvalidSubnetException {
- final long currentTime = mClock.elapsedRealtime();
- final long expTime = currentTime + mLeaseTimeMs;
-
- removeExpiredLeases(currentTime);
- checkValidRelayAddr(relayAddr);
-
- final DhcpLease currentLease = findByClient(clientId, hwAddr);
- final DhcpLease newLease;
- if (currentLease != null) {
- newLease = currentLease.renewedLease(expTime, hostname);
- mLog.log("Offering extended lease " + newLease);
- // Do not update lease time in the map: the offer is not committed yet.
- } else if (reqAddr != null && isValidAddress(reqAddr) && isAvailable(reqAddr)) {
- newLease = new DhcpLease(clientId, hwAddr, reqAddr, expTime, hostname);
- mLog.log("Offering requested lease " + newLease);
- } else {
- newLease = makeNewOffer(clientId, hwAddr, expTime, hostname);
- mLog.log("Offering new generated lease " + newLease);
- }
- return newLease;
- }
-
- private void checkValidRelayAddr(@Nullable Inet4Address relayAddr)
- throws InvalidSubnetException {
- // As per #4.3.1, addresses are assigned based on the relay address if present. This
- // implementation only assigns addresses if the relayAddr is inside our configured subnet.
- // This also applies when the client requested a specific address for consistency between
- // requests, and with older behavior.
- if (isIpAddrOutsidePrefix(mPrefix, relayAddr)) {
- throw new InvalidSubnetException("Lease requested by relay from outside of subnet");
- }
- }
-
- private static boolean isIpAddrOutsidePrefix(@NonNull IpPrefix prefix,
- @Nullable Inet4Address addr) {
- return addr != null && !addr.equals(IPV4_ADDR_ANY) && !prefix.contains(addr);
- }
-
- @Nullable
- private DhcpLease findByClient(@Nullable byte[] clientId, @NonNull MacAddress hwAddr) {
- for (DhcpLease lease : mCommittedLeases.values()) {
- if (lease.matchesClient(clientId, hwAddr)) {
- return lease;
- }
- }
-
- // Note this differs from dnsmasq behavior, which would match by hwAddr if clientId was
- // given but no lease keyed on clientId matched. This would prevent one interface from
- // obtaining multiple leases with different clientId.
- return null;
- }
-
- /**
- * Make a lease conformant to a client DHCPREQUEST or renew the client's existing lease,
- * commit it to the repository and return it.
- *
- * <p>This method always succeeds and commits the lease if it does not throw, and has no side
- * effects if it throws.
- *
- * @param clientId Client identifier option if specified, or {@link #CLIENTID_UNSPEC}
- * @param reqAddr Requested address by the client (option 50), or {@link #INETADDR_UNSPEC}
- * @param sidSet Whether the server identifier was set in the request
- * @return The newly created or renewed lease
- * @throws InvalidAddressException The client provided an address that conflicts with its
- * current configuration, or other committed/reserved leases.
- */
- @NonNull
- public DhcpLease requestLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr,
- @NonNull Inet4Address clientAddr, @NonNull Inet4Address relayAddr,
- @Nullable Inet4Address reqAddr, boolean sidSet, @Nullable String hostname)
- throws InvalidAddressException, InvalidSubnetException {
- final long currentTime = mClock.elapsedRealtime();
- removeExpiredLeases(currentTime);
- checkValidRelayAddr(relayAddr);
- final DhcpLease assignedLease = findByClient(clientId, hwAddr);
-
- final Inet4Address leaseAddr = reqAddr != null ? reqAddr : clientAddr;
- if (assignedLease != null) {
- if (sidSet && reqAddr != null) {
- // Client in SELECTING state; remove any current lease before creating a new one.
- mCommittedLeases.remove(assignedLease.getNetAddr());
- } else if (!assignedLease.getNetAddr().equals(leaseAddr)) {
- // reqAddr null (RENEWING/REBINDING): client renewing its own lease for clientAddr.
- // reqAddr set with sid not set (INIT-REBOOT): client verifying configuration.
- // In both cases, throw if clientAddr or reqAddr does not match the known lease.
- throw new InvalidAddressException("Incorrect address for client in "
- + (reqAddr != null ? "INIT-REBOOT" : "RENEWING/REBINDING"));
- }
- }
-
- // In the init-reboot case, RFC2131 #4.3.2 says that the server must not reply if
- // assignedLease == null, but dnsmasq will let the client use the requested address if
- // available, when configured with --dhcp-authoritative. This is preferable to avoid issues
- // if the server lost the lease DB: the client would not get a reply because the server
- // does not know their lease.
- // Similarly in RENEWING/REBINDING state, create a lease when possible if the
- // client-provided lease is unknown.
- final DhcpLease lease =
- checkClientAndMakeLease(clientId, hwAddr, leaseAddr, hostname, currentTime);
- mLog.logf("DHCPREQUEST assignedLease %s, reqAddr=%s, sidSet=%s: created/renewed lease %s",
- assignedLease, inet4AddrToString(reqAddr), sidSet, lease);
- return lease;
- }
-
- /**
- * Check that the client can request the specified address, make or renew the lease if yes, and
- * commit it.
- *
- * <p>This method always succeeds and returns the lease if it does not throw, and has no
- * side-effect if it throws.
- *
- * @return The newly created or renewed, committed lease
- * @throws InvalidAddressException The client provided an address that conflicts with its
- * current configuration, or other committed/reserved leases.
- */
- private DhcpLease checkClientAndMakeLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr,
- @NonNull Inet4Address addr, @Nullable String hostname, long currentTime)
- throws InvalidAddressException {
- final long expTime = currentTime + mLeaseTimeMs;
- final DhcpLease currentLease = mCommittedLeases.getOrDefault(addr, null);
- if (currentLease != null && !currentLease.matchesClient(clientId, hwAddr)) {
- throw new InvalidAddressException("Address in use");
- }
-
- final DhcpLease lease;
- if (currentLease == null) {
- if (isValidAddress(addr) && !mReservedAddrs.contains(addr)) {
- lease = new DhcpLease(clientId, hwAddr, addr, expTime, hostname);
- } else {
- throw new InvalidAddressException("Lease not found and address unavailable");
- }
- } else {
- lease = currentLease.renewedLease(expTime, hostname);
- }
- commitLease(lease);
- return lease;
- }
-
- private void commitLease(@NonNull DhcpLease lease) {
- mCommittedLeases.put(lease.getNetAddr(), lease);
- maybeUpdateEarliestExpiration(lease.getExpTime());
- }
-
- /**
- * Delete a committed lease from the repository.
- *
- * @return true if a lease matching parameters was found.
- */
- public boolean releaseLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr,
- @NonNull Inet4Address addr) {
- final DhcpLease currentLease = mCommittedLeases.getOrDefault(addr, null);
- if (currentLease == null) {
- mLog.w("Could not release unknown lease for " + inet4AddrToString(addr));
- return false;
- }
- if (currentLease.matchesClient(clientId, hwAddr)) {
- mCommittedLeases.remove(addr);
- mLog.log("Released lease " + currentLease);
- return true;
- }
- mLog.w(String.format("Not releasing lease %s: does not match client (cid %s, hwAddr %s)",
- currentLease, DhcpLease.clientIdToString(clientId), hwAddr));
- return false;
- }
-
- public void markLeaseDeclined(@NonNull Inet4Address addr) {
- if (mDeclinedAddrs.containsKey(addr) || !isValidAddress(addr)) {
- mLog.logf("Not marking %s as declined: already declined or not assignable",
- inet4AddrToString(addr));
- return;
- }
- final long expTime = mClock.elapsedRealtime() + mLeaseTimeMs;
- mDeclinedAddrs.put(addr, expTime);
- mLog.logf("Marked %s as declined expiring %d", inet4AddrToString(addr), expTime);
- maybeUpdateEarliestExpiration(expTime);
- }
-
- /**
- * Get the list of currently valid committed leases in the repository.
- */
- @NonNull
- public List<DhcpLease> getCommittedLeases() {
- removeExpiredLeases(mClock.elapsedRealtime());
- return new ArrayList<>(mCommittedLeases.values());
- }
-
- /**
- * Get the set of addresses that have been marked as declined in the repository.
- */
- @NonNull
- public Set<Inet4Address> getDeclinedAddresses() {
- removeExpiredLeases(mClock.elapsedRealtime());
- return new HashSet<>(mDeclinedAddrs.keySet());
- }
-
- /**
- * Given the expiration time of a new committed lease or declined address, update
- * {@link #mNextExpirationCheck} so it stays lower than or equal to the time for the first lease
- * to expire.
- */
- private void maybeUpdateEarliestExpiration(long expTime) {
- if (expTime < mNextExpirationCheck) {
- mNextExpirationCheck = expTime;
- }
- }
-
- /**
- * Remove expired entries from a map keyed by {@link Inet4Address}.
- *
- * @param tag Type of lease in the map, for logging
- * @param getExpTime Functor returning the expiration time for an object in the map.
- * Must not return null.
- * @return The lowest expiration time among entries remaining in the map
- */
- private <T> long removeExpired(long currentTime, @NonNull Map<Inet4Address, T> map,
- @NonNull String tag, @NonNull Function<T, Long> getExpTime) {
- final Iterator<Entry<Inet4Address, T>> it = map.entrySet().iterator();
- long firstExpiration = EXPIRATION_NEVER;
- while (it.hasNext()) {
- final Entry<Inet4Address, T> lease = it.next();
- final long expTime = getExpTime.apply(lease.getValue());
- if (expTime <= currentTime) {
- mLog.logf("Removing expired %s lease for %s (expTime=%s, currentTime=%s)",
- tag, lease.getKey(), expTime, currentTime);
- it.remove();
- } else {
- firstExpiration = min(firstExpiration, expTime);
- }
- }
- return firstExpiration;
- }
-
- /**
- * Go through committed and declined leases and remove the expired ones.
- */
- private void removeExpiredLeases(long currentTime) {
- if (currentTime < mNextExpirationCheck) {
- return;
- }
-
- final long commExp = removeExpired(
- currentTime, mCommittedLeases, "committed", DhcpLease::getExpTime);
- final long declExp = removeExpired(
- currentTime, mDeclinedAddrs, "declined", Function.identity());
-
- mNextExpirationCheck = min(commExp, declExp);
- }
-
- private boolean isAvailable(@NonNull Inet4Address addr) {
- return !mReservedAddrs.contains(addr) && !mCommittedLeases.containsKey(addr);
- }
-
- /**
- * Get the 0-based index of an address in the subnet.
- *
- * <p>Given ordering of addresses 5.6.7.8 < 5.6.7.9 < 5.6.8.0, the index on a subnet is defined
- * so that the first address is 0, the second 1, etc. For example on a /16, 192.168.0.0 -> 0,
- * 192.168.0.1 -> 1, 192.168.1.0 -> 256
- *
- */
- private int getAddrIndex(int addr) {
- return addr & ~mSubnetMask;
- }
-
- private int getAddrByIndex(int index) {
- return mSubnetAddr | index;
- }
-
- /**
- * Get a valid address starting from the supplied one.
- *
- * <p>This only checks that the address is numerically valid for assignment, not whether it is
- * already in use. The return value is always inside the configured prefix, even if the supplied
- * address is not.
- *
- * <p>If the provided address is valid, it is returned as-is. Otherwise, the next valid
- * address (with the ordering in {@link #getAddrIndex(int)}) is returned.
- */
- private int getValidAddress(int addr) {
- final int lastByteMask = 0xff;
- int addrIndex = getAddrIndex(addr); // 0-based index of the address in the subnet
-
- // Some OSes do not handle addresses in .255 or .0 correctly: avoid those.
- final int lastByte = getAddrByIndex(addrIndex) & lastByteMask;
- if (lastByte == lastByteMask) {
- // Avoid .255 address, and .0 address that follows
- addrIndex = (addrIndex + 2) % mNumAddresses;
- } else if (lastByte == 0) {
- // Avoid .0 address
- addrIndex = (addrIndex + 1) % mNumAddresses;
- }
-
- // Do not use first or last address of range
- if (addrIndex == 0 || addrIndex == mNumAddresses - 1) {
- // Always valid and not end of range since prefixLength is at most 30 in serving params
- addrIndex = 1;
- }
- return getAddrByIndex(addrIndex);
- }
-
- /**
- * Returns whether the address is in the configured subnet and part of the assignable range.
- */
- private boolean isValidAddress(Inet4Address addr) {
- final int intAddr = inet4AddressToIntHTH(addr);
- return getValidAddress(intAddr) == intAddr;
- }
-
- private int getNextAddress(int addr) {
- final int addrIndex = getAddrIndex(addr);
- final int nextAddress = getAddrByIndex((addrIndex + 1) % mNumAddresses);
- return getValidAddress(nextAddress);
- }
-
- /**
- * Calculate a first candidate address for a client by hashing the hardware address.
- *
- * <p>This will be a valid address as checked by {@link #getValidAddress(int)}, but may be
- * in use.
- *
- * @return An IPv4 address encoded as 32-bit int
- */
- private int getFirstClientAddress(MacAddress hwAddr) {
- // This follows dnsmasq behavior. Advantages are: clients will often get the same
- // offers for different DISCOVER even if the lease was not yet accepted or has expired,
- // and address generation will generally not need to loop through many allocated addresses
- // until it finds a free one.
- int hash = 0;
- for (byte b : hwAddr.toByteArray()) {
- hash += b + (b << 8) + (b << 16);
- }
- // This implementation will not always result in the same IPs as dnsmasq would give out in
- // Android <= P, because it includes invalid and reserved addresses in mNumAddresses while
- // the configured ranges for dnsmasq did not.
- final int addrIndex = hash % mNumAddresses;
- return getValidAddress(getAddrByIndex(addrIndex));
- }
-
- /**
- * Create a lease that can be offered to respond to a client DISCOVER.
- *
- * <p>This method always succeeds and returns the lease if it does not throw. If no non-declined
- * address is available, it will try to offer the oldest declined address if valid.
- *
- * @throws OutOfAddressesException The server has no address left to offer
- */
- private DhcpLease makeNewOffer(@Nullable byte[] clientId, @NonNull MacAddress hwAddr,
- long expTime, @Nullable String hostname) throws OutOfAddressesException {
- int intAddr = getFirstClientAddress(hwAddr);
- // Loop until a free address is found, or there are no more addresses.
- // There is slightly less than this many usable addresses, but some extra looping is OK
- for (int i = 0; i < mNumAddresses; i++) {
- final Inet4Address addr = intToInet4AddressHTH(intAddr);
- if (isAvailable(addr) && !mDeclinedAddrs.containsKey(addr)) {
- return new DhcpLease(clientId, hwAddr, addr, expTime, hostname);
- }
- intAddr = getNextAddress(intAddr);
- }
-
- // Try freeing DECLINEd addresses if out of addresses.
- final Iterator<Inet4Address> it = mDeclinedAddrs.keySet().iterator();
- while (it.hasNext()) {
- final Inet4Address addr = it.next();
- it.remove();
- mLog.logf("Out of addresses in address pool: dropped declined addr %s",
- inet4AddrToString(addr));
- // isValidAddress() is always verified for entries in mDeclinedAddrs.
- // However declined addresses may have been requested (typically by the machine that was
- // already using the address) after being declined.
- if (isAvailable(addr)) {
- return new DhcpLease(clientId, hwAddr, addr, expTime, hostname);
- }
- }
-
- throw new OutOfAddressesException("No address available for offer");
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpNakPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpNakPacket.java
deleted file mode 100644
index 1da0b73..0000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpNakPacket.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-NAK packet.
- */
-class DhcpNakPacket extends DhcpPacket {
- /**
- * Generates a NAK packet with the specified parameters.
- */
- DhcpNakPacket(int transId, short secs, Inet4Address relayIp, byte[] clientMac,
- boolean broadcast) {
- super(transId, secs, INADDR_ANY /* clientIp */, INADDR_ANY /* yourIp */,
- INADDR_ANY /* nextIp */, relayIp, clientMac, broadcast);
- }
-
- public String toString() {
- String s = super.toString();
- return s + " NAK, reason " + (mMessage == null ? "(none)" : mMessage);
- }
-
- /**
- * Fills in a packet with the requested NAK attributes.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- // Constructor does not set values for layers <= 3: use empty values
- Inet4Address destIp = INADDR_ANY;
- Inet4Address srcIp = INADDR_ANY;
-
- fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result, DHCP_BOOTREPLY, mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds the optional parameters to the client-generated NAK packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_NAK);
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
- addTlv(buffer, DHCP_MESSAGE, mMessage);
- addTlvEnd(buffer);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpOfferPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpOfferPacket.java
deleted file mode 100644
index 0eba77e..0000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpOfferPacket.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-OFFER packet.
- */
-class DhcpOfferPacket extends DhcpPacket {
- /**
- * The IP address of the server which sent this packet.
- */
- private final Inet4Address mSrcIp;
-
- /**
- * Generates a OFFER packet with the specified parameters.
- */
- DhcpOfferPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress,
- Inet4Address relayIp, Inet4Address clientIp, Inet4Address yourIp, byte[] clientMac) {
- super(transId, secs, clientIp, yourIp, serverAddress, relayIp, clientMac, broadcast);
- mSrcIp = serverAddress;
- }
-
- public String toString() {
- String s = super.toString();
- String dnsServers = ", DNS servers: ";
-
- if (mDnsServers != null) {
- for (Inet4Address dnsServer: mDnsServers) {
- dnsServers += dnsServer + " ";
- }
- }
-
- return s + " OFFER, ip " + mYourIp + ", mask " + mSubnetMask +
- dnsServers + ", gateways " + mGateways +
- " lease time " + mLeaseTime + ", domain " + mDomainName;
- }
-
- /**
- * Fills in a packet with the specified OFFER attributes.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- Inet4Address destIp = mBroadcast ? INADDR_BROADCAST : mYourIp;
- Inet4Address srcIp = mBroadcast ? INADDR_ANY : mSrcIp;
-
- fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result,
- DHCP_BOOTREPLY, mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds the optional parameters to the server-generated OFFER packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_OFFER);
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
-
- addCommonServerTlvs(buffer);
- addTlvEnd(buffer);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java
deleted file mode 100644
index a15d423..0000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java
+++ /dev/null
@@ -1,1397 +0,0 @@
-package android.net.dhcp;
-
-import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ALL;
-import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
-
-import android.annotation.Nullable;
-import android.net.DhcpResults;
-import android.net.LinkAddress;
-import android.net.metrics.DhcpErrorEvent;
-import android.net.shared.Inet4AddressUtils;
-import android.os.Build;
-import android.os.SystemProperties;
-import android.system.OsConstants;
-import android.text.TextUtils;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.UnsupportedEncodingException;
-import java.net.Inet4Address;
-import java.net.UnknownHostException;
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.ShortBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Defines basic data and operations needed to build and use packets for the
- * DHCP protocol. Subclasses create the specific packets used at each
- * stage of the negotiation.
- *
- * @hide
- */
-public abstract class DhcpPacket {
- protected static final String TAG = "DhcpPacket";
-
- // TODO: use NetworkStackConstants.IPV4_MIN_MTU once this class is moved to the network stack.
- private static final int IPV4_MIN_MTU = 68;
-
- // dhcpcd has a minimum lease of 20 seconds, but DhcpStateMachine would refuse to wake up the
- // CPU for anything shorter than 5 minutes. For sanity's sake, this must be higher than the
- // DHCP client timeout.
- public static final int MINIMUM_LEASE = 60;
- public static final int INFINITE_LEASE = (int) 0xffffffff;
-
- public static final Inet4Address INADDR_ANY = IPV4_ADDR_ANY;
- public static final Inet4Address INADDR_BROADCAST = IPV4_ADDR_ALL;
- public static final byte[] ETHER_BROADCAST = new byte[] {
- (byte) 0xff, (byte) 0xff, (byte) 0xff,
- (byte) 0xff, (byte) 0xff, (byte) 0xff,
- };
-
- /**
- * Packet encapsulations.
- */
- public static final int ENCAP_L2 = 0; // EthernetII header included
- public static final int ENCAP_L3 = 1; // IP/UDP header included
- public static final int ENCAP_BOOTP = 2; // BOOTP contents only
-
- /**
- * Minimum length of a DHCP packet, excluding options, in the above encapsulations.
- */
- public static final int MIN_PACKET_LENGTH_BOOTP = 236; // See diagram in RFC 2131, section 2.
- public static final int MIN_PACKET_LENGTH_L3 = MIN_PACKET_LENGTH_BOOTP + 20 + 8;
- public static final int MIN_PACKET_LENGTH_L2 = MIN_PACKET_LENGTH_L3 + 14;
-
- public static final int HWADDR_LEN = 16;
- public static final int MAX_OPTION_LEN = 255;
-
- /**
- * The minimum and maximum MTU that we are prepared to use. We set the minimum to the minimum
- * IPv6 MTU because the IPv6 stack enters unusual codepaths when the link MTU drops below 1280,
- * and does not recover if the MTU is brought above 1280 again. We set the maximum to 1500
- * because in general it is risky to assume that the hardware is able to send/receive packets
- * larger than 1500 bytes even if the network supports it.
- */
- private static final int MIN_MTU = 1280;
- private static final int MAX_MTU = 1500;
-
- /**
- * IP layer definitions.
- */
- private static final byte IP_TYPE_UDP = (byte) 0x11;
-
- /**
- * IP: Version 4, Header Length 20 bytes
- */
- private static final byte IP_VERSION_HEADER_LEN = (byte) 0x45;
-
- /**
- * IP: Flags 0, Fragment Offset 0, Don't Fragment
- */
- private static final short IP_FLAGS_OFFSET = (short) 0x4000;
-
- /**
- * IP: TOS
- */
- private static final byte IP_TOS_LOWDELAY = (byte) 0x10;
-
- /**
- * IP: TTL -- use default 64 from RFC1340
- */
- private static final byte IP_TTL = (byte) 0x40;
-
- /**
- * The client DHCP port.
- */
- static final short DHCP_CLIENT = (short) 68;
-
- /**
- * The server DHCP port.
- */
- static final short DHCP_SERVER = (short) 67;
-
- /**
- * The message op code indicating a request from a client.
- */
- protected static final byte DHCP_BOOTREQUEST = (byte) 1;
-
- /**
- * The message op code indicating a response from the server.
- */
- protected static final byte DHCP_BOOTREPLY = (byte) 2;
-
- /**
- * The code type used to identify an Ethernet MAC address in the
- * Client-ID field.
- */
- protected static final byte CLIENT_ID_ETHER = (byte) 1;
-
- /**
- * The maximum length of a packet that can be constructed.
- */
- protected static final int MAX_LENGTH = 1500;
-
- /**
- * The magic cookie that identifies this as a DHCP packet instead of BOOTP.
- */
- private static final int DHCP_MAGIC_COOKIE = 0x63825363;
-
- /**
- * DHCP Optional Type: DHCP Subnet Mask
- */
- protected static final byte DHCP_SUBNET_MASK = 1;
- protected Inet4Address mSubnetMask;
-
- /**
- * DHCP Optional Type: DHCP Router
- */
- protected static final byte DHCP_ROUTER = 3;
- protected List <Inet4Address> mGateways;
-
- /**
- * DHCP Optional Type: DHCP DNS Server
- */
- protected static final byte DHCP_DNS_SERVER = 6;
- protected List<Inet4Address> mDnsServers;
-
- /**
- * DHCP Optional Type: DHCP Host Name
- */
- protected static final byte DHCP_HOST_NAME = 12;
- protected String mHostName;
-
- /**
- * DHCP Optional Type: DHCP DOMAIN NAME
- */
- protected static final byte DHCP_DOMAIN_NAME = 15;
- protected String mDomainName;
-
- /**
- * DHCP Optional Type: DHCP Interface MTU
- */
- protected static final byte DHCP_MTU = 26;
- protected Short mMtu;
-
- /**
- * DHCP Optional Type: DHCP BROADCAST ADDRESS
- */
- protected static final byte DHCP_BROADCAST_ADDRESS = 28;
- protected Inet4Address mBroadcastAddress;
-
- /**
- * DHCP Optional Type: Vendor specific information
- */
- protected static final byte DHCP_VENDOR_INFO = 43;
- protected String mVendorInfo;
-
- /**
- * Value of the vendor specific option used to indicate that the network is metered
- */
- public static final String VENDOR_INFO_ANDROID_METERED = "ANDROID_METERED";
-
- /**
- * DHCP Optional Type: Option overload option
- */
- protected static final byte DHCP_OPTION_OVERLOAD = 52;
-
- /**
- * Possible values of the option overload option.
- */
- private static final byte OPTION_OVERLOAD_FILE = 1;
- private static final byte OPTION_OVERLOAD_SNAME = 2;
- private static final byte OPTION_OVERLOAD_BOTH = 3;
-
- /**
- * DHCP Optional Type: DHCP Requested IP Address
- */
- protected static final byte DHCP_REQUESTED_IP = 50;
- protected Inet4Address mRequestedIp;
-
- /**
- * DHCP Optional Type: DHCP Lease Time
- */
- protected static final byte DHCP_LEASE_TIME = 51;
- protected Integer mLeaseTime;
-
- /**
- * DHCP Optional Type: DHCP Message Type
- */
- protected static final byte DHCP_MESSAGE_TYPE = 53;
- // the actual type values
- protected static final byte DHCP_MESSAGE_TYPE_DISCOVER = 1;
- protected static final byte DHCP_MESSAGE_TYPE_OFFER = 2;
- protected static final byte DHCP_MESSAGE_TYPE_REQUEST = 3;
- protected static final byte DHCP_MESSAGE_TYPE_DECLINE = 4;
- protected static final byte DHCP_MESSAGE_TYPE_ACK = 5;
- protected static final byte DHCP_MESSAGE_TYPE_NAK = 6;
- protected static final byte DHCP_MESSAGE_TYPE_RELEASE = 7;
- protected static final byte DHCP_MESSAGE_TYPE_INFORM = 8;
-
- /**
- * DHCP Optional Type: DHCP Server Identifier
- */
- protected static final byte DHCP_SERVER_IDENTIFIER = 54;
- protected Inet4Address mServerIdentifier;
-
- /**
- * DHCP Optional Type: DHCP Parameter List
- */
- protected static final byte DHCP_PARAMETER_LIST = 55;
- protected byte[] mRequestedParams;
-
- /**
- * DHCP Optional Type: DHCP MESSAGE
- */
- protected static final byte DHCP_MESSAGE = 56;
- protected String mMessage;
-
- /**
- * DHCP Optional Type: Maximum DHCP Message Size
- */
- protected static final byte DHCP_MAX_MESSAGE_SIZE = 57;
- protected Short mMaxMessageSize;
-
- /**
- * DHCP Optional Type: DHCP Renewal Time Value
- */
- protected static final byte DHCP_RENEWAL_TIME = 58;
- protected Integer mT1;
-
- /**
- * DHCP Optional Type: Rebinding Time Value
- */
- protected static final byte DHCP_REBINDING_TIME = 59;
- protected Integer mT2;
-
- /**
- * DHCP Optional Type: Vendor Class Identifier
- */
- protected static final byte DHCP_VENDOR_CLASS_ID = 60;
- protected String mVendorId;
-
- /**
- * DHCP Optional Type: DHCP Client Identifier
- */
- protected static final byte DHCP_CLIENT_IDENTIFIER = 61;
- protected byte[] mClientId;
-
- /**
- * DHCP zero-length option code: pad
- */
- protected static final byte DHCP_OPTION_PAD = 0x00;
-
- /**
- * DHCP zero-length option code: end of options
- */
- protected static final byte DHCP_OPTION_END = (byte) 0xff;
-
- /**
- * The transaction identifier used in this particular DHCP negotiation
- */
- protected final int mTransId;
-
- /**
- * The seconds field in the BOOTP header. Per RFC, should be nonzero in client requests only.
- */
- protected final short mSecs;
-
- /**
- * The IP address of the client host. This address is typically
- * proposed by the client (from an earlier DHCP negotiation) or
- * supplied by the server.
- */
- protected final Inet4Address mClientIp;
- protected final Inet4Address mYourIp;
- private final Inet4Address mNextIp;
- protected final Inet4Address mRelayIp;
-
- /**
- * Does the client request a broadcast response?
- */
- protected boolean mBroadcast;
-
- /**
- * The six-octet MAC of the client.
- */
- protected final byte[] mClientMac;
-
- /**
- * The server host name from server.
- */
- protected String mServerHostName;
-
- /**
- * Asks the packet object to create a ByteBuffer serialization of
- * the packet for transmission.
- */
- public abstract ByteBuffer buildPacket(int encap, short destUdp,
- short srcUdp);
-
- /**
- * Allows the concrete class to fill in packet-type-specific details,
- * typically optional parameters at the end of the packet.
- */
- abstract void finishPacket(ByteBuffer buffer);
-
- // Set in unit tests, to ensure that the test does not break when run on different devices and
- // on different releases.
- static String testOverrideVendorId = null;
- static String testOverrideHostname = null;
-
- protected DhcpPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
- Inet4Address nextIp, Inet4Address relayIp,
- byte[] clientMac, boolean broadcast) {
- mTransId = transId;
- mSecs = secs;
- mClientIp = clientIp;
- mYourIp = yourIp;
- mNextIp = nextIp;
- mRelayIp = relayIp;
- mClientMac = clientMac;
- mBroadcast = broadcast;
- }
-
- /**
- * Returns the transaction ID.
- */
- public int getTransactionId() {
- return mTransId;
- }
-
- /**
- * Returns the client MAC.
- */
- public byte[] getClientMac() {
- return mClientMac;
- }
-
- // TODO: refactor DhcpClient to set clientId when constructing packets and remove
- // hasExplicitClientId logic
- /**
- * Returns whether a client ID was set in the options for this packet.
- */
- public boolean hasExplicitClientId() {
- return mClientId != null;
- }
-
- /**
- * Convenience method to return the client ID if it was set explicitly, or null otherwise.
- */
- @Nullable
- public byte[] getExplicitClientIdOrNull() {
- return hasExplicitClientId() ? getClientId() : null;
- }
-
- /**
- * Returns the client ID. If not set explicitly, this follows RFC 2132 and creates a client ID
- * based on the hardware address.
- */
- public byte[] getClientId() {
- final byte[] clientId;
- if (hasExplicitClientId()) {
- clientId = Arrays.copyOf(mClientId, mClientId.length);
- } else {
- clientId = new byte[mClientMac.length + 1];
- clientId[0] = CLIENT_ID_ETHER;
- System.arraycopy(mClientMac, 0, clientId, 1, mClientMac.length);
- }
- return clientId;
- }
-
- /**
- * Returns whether a parameter is included in the parameter request list option of this packet.
- *
- * <p>If there is no parameter request list option in the packet, false is returned.
- *
- * @param paramId ID of the parameter, such as {@link #DHCP_MTU} or {@link #DHCP_HOST_NAME}.
- */
- public boolean hasRequestedParam(byte paramId) {
- if (mRequestedParams == null) {
- return false;
- }
-
- for (byte reqParam : mRequestedParams) {
- if (reqParam == paramId) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Creates a new L3 packet (including IP header) containing the
- * DHCP udp packet. This method relies upon the delegated method
- * finishPacket() to insert the per-packet contents.
- */
- protected void fillInPacket(int encap, Inet4Address destIp,
- Inet4Address srcIp, short destUdp, short srcUdp, ByteBuffer buf,
- byte requestCode, boolean broadcast) {
- byte[] destIpArray = destIp.getAddress();
- byte[] srcIpArray = srcIp.getAddress();
- int ipHeaderOffset = 0;
- int ipLengthOffset = 0;
- int ipChecksumOffset = 0;
- int endIpHeader = 0;
- int udpHeaderOffset = 0;
- int udpLengthOffset = 0;
- int udpChecksumOffset = 0;
-
- buf.clear();
- buf.order(ByteOrder.BIG_ENDIAN);
-
- if (encap == ENCAP_L2) {
- buf.put(ETHER_BROADCAST);
- buf.put(mClientMac);
- buf.putShort((short) OsConstants.ETH_P_IP);
- }
-
- // if a full IP packet needs to be generated, put the IP & UDP
- // headers in place, and pre-populate with artificial values
- // needed to seed the IP checksum.
- if (encap <= ENCAP_L3) {
- ipHeaderOffset = buf.position();
- buf.put(IP_VERSION_HEADER_LEN);
- buf.put(IP_TOS_LOWDELAY); // tos: IPTOS_LOWDELAY
- ipLengthOffset = buf.position();
- buf.putShort((short)0); // length
- buf.putShort((short)0); // id
- buf.putShort(IP_FLAGS_OFFSET); // ip offset: don't fragment
- buf.put(IP_TTL); // TTL: use default 64 from RFC1340
- buf.put(IP_TYPE_UDP);
- ipChecksumOffset = buf.position();
- buf.putShort((short) 0); // checksum
-
- buf.put(srcIpArray);
- buf.put(destIpArray);
- endIpHeader = buf.position();
-
- // UDP header
- udpHeaderOffset = buf.position();
- buf.putShort(srcUdp);
- buf.putShort(destUdp);
- udpLengthOffset = buf.position();
- buf.putShort((short) 0); // length
- udpChecksumOffset = buf.position();
- buf.putShort((short) 0); // UDP checksum -- initially zero
- }
-
- // DHCP payload
- buf.put(requestCode);
- buf.put((byte) 1); // Hardware Type: Ethernet
- buf.put((byte) mClientMac.length); // Hardware Address Length
- buf.put((byte) 0); // Hop Count
- buf.putInt(mTransId); // Transaction ID
- buf.putShort(mSecs); // Elapsed Seconds
-
- if (broadcast) {
- buf.putShort((short) 0x8000); // Flags
- } else {
- buf.putShort((short) 0x0000); // Flags
- }
-
- buf.put(mClientIp.getAddress());
- buf.put(mYourIp.getAddress());
- buf.put(mNextIp.getAddress());
- buf.put(mRelayIp.getAddress());
- buf.put(mClientMac);
- buf.position(buf.position() +
- (HWADDR_LEN - mClientMac.length) // pad addr to 16 bytes
- + 64 // empty server host name (64 bytes)
- + 128); // empty boot file name (128 bytes)
- buf.putInt(DHCP_MAGIC_COOKIE); // magic number
- finishPacket(buf);
-
- // round up to an even number of octets
- if ((buf.position() & 1) == 1) {
- buf.put((byte) 0);
- }
-
- // If an IP packet is being built, the IP & UDP checksums must be
- // computed.
- if (encap <= ENCAP_L3) {
- // fix UDP header: insert length
- short udpLen = (short)(buf.position() - udpHeaderOffset);
- buf.putShort(udpLengthOffset, udpLen);
- // fix UDP header: checksum
- // checksum for UDP at udpChecksumOffset
- int udpSeed = 0;
-
- // apply IPv4 pseudo-header. Read IP address src and destination
- // values from the IP header and accumulate checksum.
- udpSeed += intAbs(buf.getShort(ipChecksumOffset + 2));
- udpSeed += intAbs(buf.getShort(ipChecksumOffset + 4));
- udpSeed += intAbs(buf.getShort(ipChecksumOffset + 6));
- udpSeed += intAbs(buf.getShort(ipChecksumOffset + 8));
-
- // accumulate extra data for the pseudo-header
- udpSeed += IP_TYPE_UDP;
- udpSeed += udpLen;
- // and compute UDP checksum
- buf.putShort(udpChecksumOffset, (short) checksum(buf, udpSeed,
- udpHeaderOffset,
- buf.position()));
- // fix IP header: insert length
- buf.putShort(ipLengthOffset, (short)(buf.position() - ipHeaderOffset));
- // fixup IP-header checksum
- buf.putShort(ipChecksumOffset,
- (short) checksum(buf, 0, ipHeaderOffset, endIpHeader));
- }
- }
-
- /**
- * Converts a signed short value to an unsigned int value. Needed
- * because Java does not have unsigned types.
- */
- private static int intAbs(short v) {
- return v & 0xFFFF;
- }
-
- /**
- * Performs an IP checksum (used in IP header and across UDP
- * payload) on the specified portion of a ByteBuffer. The seed
- * allows the checksum to commence with a specified value.
- */
- private int checksum(ByteBuffer buf, int seed, int start, int end) {
- int sum = seed;
- int bufPosition = buf.position();
-
- // set position of original ByteBuffer, so that the ShortBuffer
- // will be correctly initialized
- buf.position(start);
- ShortBuffer shortBuf = buf.asShortBuffer();
-
- // re-set ByteBuffer position
- buf.position(bufPosition);
-
- short[] shortArray = new short[(end - start) / 2];
- shortBuf.get(shortArray);
-
- for (short s : shortArray) {
- sum += intAbs(s);
- }
-
- start += shortArray.length * 2;
-
- // see if a singleton byte remains
- if (end != start) {
- short b = buf.get(start);
-
- // make it unsigned
- if (b < 0) {
- b += 256;
- }
-
- sum += b * 256;
- }
-
- sum = ((sum >> 16) & 0xFFFF) + (sum & 0xFFFF);
- sum = ((sum + ((sum >> 16) & 0xFFFF)) & 0xFFFF);
- int negated = ~sum;
- return intAbs((short) negated);
- }
-
- /**
- * Adds an optional parameter containing a single byte value.
- */
- protected static void addTlv(ByteBuffer buf, byte type, byte value) {
- buf.put(type);
- buf.put((byte) 1);
- buf.put(value);
- }
-
- /**
- * Adds an optional parameter containing an array of bytes.
- *
- * <p>This method is a no-op if the payload argument is null.
- */
- protected static void addTlv(ByteBuffer buf, byte type, @Nullable byte[] payload) {
- if (payload != null) {
- if (payload.length > MAX_OPTION_LEN) {
- throw new IllegalArgumentException("DHCP option too long: "
- + payload.length + " vs. " + MAX_OPTION_LEN);
- }
- buf.put(type);
- buf.put((byte) payload.length);
- buf.put(payload);
- }
- }
-
- /**
- * Adds an optional parameter containing an IP address.
- *
- * <p>This method is a no-op if the address argument is null.
- */
- protected static void addTlv(ByteBuffer buf, byte type, @Nullable Inet4Address addr) {
- if (addr != null) {
- addTlv(buf, type, addr.getAddress());
- }
- }
-
- /**
- * Adds an optional parameter containing a list of IP addresses.
- *
- * <p>This method is a no-op if the addresses argument is null or empty.
- */
- protected static void addTlv(ByteBuffer buf, byte type, @Nullable List<Inet4Address> addrs) {
- if (addrs == null || addrs.size() == 0) return;
-
- int optionLen = 4 * addrs.size();
- if (optionLen > MAX_OPTION_LEN) {
- throw new IllegalArgumentException("DHCP option too long: "
- + optionLen + " vs. " + MAX_OPTION_LEN);
- }
-
- buf.put(type);
- buf.put((byte)(optionLen));
-
- for (Inet4Address addr : addrs) {
- buf.put(addr.getAddress());
- }
- }
-
- /**
- * Adds an optional parameter containing a short integer.
- *
- * <p>This method is a no-op if the value argument is null.
- */
- protected static void addTlv(ByteBuffer buf, byte type, @Nullable Short value) {
- if (value != null) {
- buf.put(type);
- buf.put((byte) 2);
- buf.putShort(value.shortValue());
- }
- }
-
- /**
- * Adds an optional parameter containing a simple integer.
- *
- * <p>This method is a no-op if the value argument is null.
- */
- protected static void addTlv(ByteBuffer buf, byte type, @Nullable Integer value) {
- if (value != null) {
- buf.put(type);
- buf.put((byte) 4);
- buf.putInt(value.intValue());
- }
- }
-
- /**
- * Adds an optional parameter containing an ASCII string.
- *
- * <p>This method is a no-op if the string argument is null.
- */
- protected static void addTlv(ByteBuffer buf, byte type, @Nullable String str) {
- if (str != null) {
- try {
- addTlv(buf, type, str.getBytes("US-ASCII"));
- } catch (UnsupportedEncodingException e) {
- throw new IllegalArgumentException("String is not US-ASCII: " + str);
- }
- }
- }
-
- /**
- * Adds the special end-of-optional-parameters indicator.
- */
- protected static void addTlvEnd(ByteBuffer buf) {
- buf.put((byte) 0xFF);
- }
-
- private String getVendorId() {
- if (testOverrideVendorId != null) return testOverrideVendorId;
- return "android-dhcp-" + Build.VERSION.RELEASE;
- }
-
- private String getHostname() {
- if (testOverrideHostname != null) return testOverrideHostname;
- return SystemProperties.get("net.hostname");
- }
-
- /**
- * Adds common client TLVs.
- *
- * TODO: Does this belong here? The alternative would be to modify all the buildXyzPacket
- * methods to take them.
- */
- protected void addCommonClientTlvs(ByteBuffer buf) {
- addTlv(buf, DHCP_MAX_MESSAGE_SIZE, (short) MAX_LENGTH);
- addTlv(buf, DHCP_VENDOR_CLASS_ID, getVendorId());
- final String hn = getHostname();
- if (!TextUtils.isEmpty(hn)) addTlv(buf, DHCP_HOST_NAME, hn);
- }
-
- protected void addCommonServerTlvs(ByteBuffer buf) {
- addTlv(buf, DHCP_LEASE_TIME, mLeaseTime);
- if (mLeaseTime != null && mLeaseTime != INFINITE_LEASE) {
- // The client should renew at 1/2 the lease-expiry interval
- addTlv(buf, DHCP_RENEWAL_TIME, (int) (Integer.toUnsignedLong(mLeaseTime) / 2));
- // Default rebinding time is set as below by RFC2131
- addTlv(buf, DHCP_REBINDING_TIME,
- (int) (Integer.toUnsignedLong(mLeaseTime) * 875L / 1000L));
- }
- addTlv(buf, DHCP_SUBNET_MASK, mSubnetMask);
- addTlv(buf, DHCP_BROADCAST_ADDRESS, mBroadcastAddress);
- addTlv(buf, DHCP_ROUTER, mGateways);
- addTlv(buf, DHCP_DNS_SERVER, mDnsServers);
- addTlv(buf, DHCP_DOMAIN_NAME, mDomainName);
- addTlv(buf, DHCP_HOST_NAME, mHostName);
- addTlv(buf, DHCP_VENDOR_INFO, mVendorInfo);
- if (mMtu != null && Short.toUnsignedInt(mMtu) >= IPV4_MIN_MTU) {
- addTlv(buf, DHCP_MTU, mMtu);
- }
- }
-
- /**
- * Converts a MAC from an array of octets to an ASCII string.
- */
- public static String macToString(byte[] mac) {
- String macAddr = "";
-
- for (int i = 0; i < mac.length; i++) {
- String hexString = "0" + Integer.toHexString(mac[i]);
-
- // substring operation grabs the last 2 digits: this
- // allows signed bytes to be converted correctly.
- macAddr += hexString.substring(hexString.length() - 2);
-
- if (i != (mac.length - 1)) {
- macAddr += ":";
- }
- }
-
- return macAddr;
- }
-
- public String toString() {
- String macAddr = macToString(mClientMac);
-
- return macAddr;
- }
-
- /**
- * Reads a four-octet value from a ByteBuffer and construct
- * an IPv4 address from that value.
- */
- private static Inet4Address readIpAddress(ByteBuffer packet) {
- Inet4Address result = null;
- byte[] ipAddr = new byte[4];
- packet.get(ipAddr);
-
- try {
- result = (Inet4Address) Inet4Address.getByAddress(ipAddr);
- } catch (UnknownHostException ex) {
- // ipAddr is numeric, so this should not be
- // triggered. However, if it is, just nullify
- result = null;
- }
-
- return result;
- }
-
- /**
- * Reads a string of specified length from the buffer.
- */
- private static String readAsciiString(ByteBuffer buf, int byteCount, boolean nullOk) {
- byte[] bytes = new byte[byteCount];
- buf.get(bytes);
- int length = bytes.length;
- if (!nullOk) {
- // Stop at the first null byte. This is because some DHCP options (e.g., the domain
- // name) are passed to netd via FrameworkListener, which refuses arguments containing
- // null bytes. We don't do this by default because vendorInfo is an opaque string which
- // could in theory contain null bytes.
- for (length = 0; length < bytes.length; length++) {
- if (bytes[length] == 0) {
- break;
- }
- }
- }
- return new String(bytes, 0, length, StandardCharsets.US_ASCII);
- }
-
- private static boolean isPacketToOrFromClient(short udpSrcPort, short udpDstPort) {
- return (udpSrcPort == DHCP_CLIENT) || (udpDstPort == DHCP_CLIENT);
- }
-
- private static boolean isPacketServerToServer(short udpSrcPort, short udpDstPort) {
- return (udpSrcPort == DHCP_SERVER) && (udpDstPort == DHCP_SERVER);
- }
-
- public static class ParseException extends Exception {
- public final int errorCode;
- public ParseException(int errorCode, String msg, Object... args) {
- super(String.format(msg, args));
- this.errorCode = errorCode;
- }
- }
-
- /**
- * Creates a concrete DhcpPacket from the supplied ByteBuffer. The
- * buffer may have an L2 encapsulation (which is the full EthernetII
- * format starting with the source-address MAC) or an L3 encapsulation
- * (which starts with the IP header).
- * <br>
- * A subset of the optional parameters are parsed and are stored
- * in object fields.
- */
- @VisibleForTesting
- static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) throws ParseException
- {
- // bootp parameters
- int transactionId;
- short secs;
- Inet4Address clientIp;
- Inet4Address yourIp;
- Inet4Address nextIp;
- Inet4Address relayIp;
- byte[] clientMac;
- byte[] clientId = null;
- List<Inet4Address> dnsServers = new ArrayList<>();
- List<Inet4Address> gateways = new ArrayList<>(); // aka router
- Inet4Address serverIdentifier = null;
- Inet4Address netMask = null;
- String message = null;
- String vendorId = null;
- String vendorInfo = null;
- byte[] expectedParams = null;
- String hostName = null;
- String domainName = null;
- Inet4Address ipSrc = null;
- Inet4Address ipDst = null;
- Inet4Address bcAddr = null;
- Inet4Address requestedIp = null;
- String serverHostName;
- byte optionOverload = 0;
-
- // The following are all unsigned integers. Internally we store them as signed integers of
- // the same length because that way we're guaranteed that they can't be out of the range of
- // the unsigned field in the packet. Callers wanting to pass in an unsigned value will need
- // to cast it.
- Short mtu = null;
- Short maxMessageSize = null;
- Integer leaseTime = null;
- Integer T1 = null;
- Integer T2 = null;
-
- // dhcp options
- byte dhcpType = (byte) 0xFF;
-
- packet.order(ByteOrder.BIG_ENDIAN);
-
- // check to see if we need to parse L2, IP, and UDP encaps
- if (pktType == ENCAP_L2) {
- if (packet.remaining() < MIN_PACKET_LENGTH_L2) {
- throw new ParseException(DhcpErrorEvent.L2_TOO_SHORT,
- "L2 packet too short, %d < %d", packet.remaining(), MIN_PACKET_LENGTH_L2);
- }
-
- byte[] l2dst = new byte[6];
- byte[] l2src = new byte[6];
-
- packet.get(l2dst);
- packet.get(l2src);
-
- short l2type = packet.getShort();
-
- if (l2type != OsConstants.ETH_P_IP) {
- throw new ParseException(DhcpErrorEvent.L2_WRONG_ETH_TYPE,
- "Unexpected L2 type 0x%04x, expected 0x%04x", l2type, OsConstants.ETH_P_IP);
- }
- }
-
- if (pktType <= ENCAP_L3) {
- if (packet.remaining() < MIN_PACKET_LENGTH_L3) {
- throw new ParseException(DhcpErrorEvent.L3_TOO_SHORT,
- "L3 packet too short, %d < %d", packet.remaining(), MIN_PACKET_LENGTH_L3);
- }
-
- byte ipTypeAndLength = packet.get();
- int ipVersion = (ipTypeAndLength & 0xf0) >> 4;
- if (ipVersion != 4) {
- throw new ParseException(
- DhcpErrorEvent.L3_NOT_IPV4, "Invalid IP version %d", ipVersion);
- }
-
- // System.out.println("ipType is " + ipType);
- byte ipDiffServicesField = packet.get();
- short ipTotalLength = packet.getShort();
- short ipIdentification = packet.getShort();
- byte ipFlags = packet.get();
- byte ipFragOffset = packet.get();
- byte ipTTL = packet.get();
- byte ipProto = packet.get();
- short ipChksm = packet.getShort();
-
- ipSrc = readIpAddress(packet);
- ipDst = readIpAddress(packet);
-
- if (ipProto != IP_TYPE_UDP) {
- throw new ParseException(
- DhcpErrorEvent.L4_NOT_UDP, "Protocol not UDP: %d", ipProto);
- }
-
- // Skip options. This cannot cause us to read beyond the end of the buffer because the
- // IPv4 header cannot be more than (0x0f * 4) = 60 bytes long, and that is less than
- // MIN_PACKET_LENGTH_L3.
- int optionWords = ((ipTypeAndLength & 0x0f) - 5);
- for (int i = 0; i < optionWords; i++) {
- packet.getInt();
- }
-
- // assume UDP
- short udpSrcPort = packet.getShort();
- short udpDstPort = packet.getShort();
- short udpLen = packet.getShort();
- short udpChkSum = packet.getShort();
-
- // Only accept packets to or from the well-known client port (expressly permitting
- // packets from ports other than the well-known server port; http://b/24687559), and
- // server-to-server packets, e.g. for relays.
- if (!isPacketToOrFromClient(udpSrcPort, udpDstPort) &&
- !isPacketServerToServer(udpSrcPort, udpDstPort)) {
- // This should almost never happen because we use SO_ATTACH_FILTER on the packet
- // socket to drop packets that don't have the right source ports. However, it's
- // possible that a packet arrives between when the socket is bound and when the
- // filter is set. http://b/26696823 .
- throw new ParseException(DhcpErrorEvent.L4_WRONG_PORT,
- "Unexpected UDP ports %d->%d", udpSrcPort, udpDstPort);
- }
- }
-
- // We need to check the length even for ENCAP_L3 because the IPv4 header is variable-length.
- if (pktType > ENCAP_BOOTP || packet.remaining() < MIN_PACKET_LENGTH_BOOTP) {
- throw new ParseException(DhcpErrorEvent.BOOTP_TOO_SHORT,
- "Invalid type or BOOTP packet too short, %d < %d",
- packet.remaining(), MIN_PACKET_LENGTH_BOOTP);
- }
-
- byte type = packet.get();
- byte hwType = packet.get();
- int addrLen = packet.get() & 0xff;
- byte hops = packet.get();
- transactionId = packet.getInt();
- secs = packet.getShort();
- short bootpFlags = packet.getShort();
- boolean broadcast = (bootpFlags & 0x8000) != 0;
- byte[] ipv4addr = new byte[4];
-
- try {
- packet.get(ipv4addr);
- clientIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
- packet.get(ipv4addr);
- yourIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
- packet.get(ipv4addr);
- nextIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
- packet.get(ipv4addr);
- relayIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
- } catch (UnknownHostException ex) {
- throw new ParseException(DhcpErrorEvent.L3_INVALID_IP,
- "Invalid IPv4 address: %s", Arrays.toString(ipv4addr));
- }
-
- // Some DHCP servers have been known to announce invalid client hardware address values such
- // as 0xff. The legacy DHCP client accepted these becuause it does not check the length at
- // all but only checks that the interface MAC address matches the first bytes of the address
- // in the packets. We're a bit stricter: if the length is obviously invalid (i.e., bigger
- // than the size of the field), we fudge it to 6 (Ethernet). http://b/23725795
- // TODO: evaluate whether to make this test more liberal.
- if (addrLen > HWADDR_LEN) {
- addrLen = ETHER_BROADCAST.length;
- }
-
- clientMac = new byte[addrLen];
- packet.get(clientMac);
-
- // skip over address padding (16 octets allocated)
- packet.position(packet.position() + (16 - addrLen));
- serverHostName = readAsciiString(packet, 64, false);
- packet.position(packet.position() + 128);
-
- // Ensure this is a DHCP packet with a magic cookie, and not BOOTP. http://b/31850211
- if (packet.remaining() < 4) {
- throw new ParseException(DhcpErrorEvent.DHCP_NO_COOKIE, "not a DHCP message");
- }
-
- int dhcpMagicCookie = packet.getInt();
- if (dhcpMagicCookie != DHCP_MAGIC_COOKIE) {
- throw new ParseException(DhcpErrorEvent.DHCP_BAD_MAGIC_COOKIE,
- "Bad magic cookie 0x%08x, should be 0x%08x",
- dhcpMagicCookie, DHCP_MAGIC_COOKIE);
- }
-
- // parse options
- boolean notFinishedOptions = true;
-
- while ((packet.position() < packet.limit()) && notFinishedOptions) {
- final byte optionType = packet.get(); // cannot underflow because position < limit
- try {
- if (optionType == DHCP_OPTION_END) {
- notFinishedOptions = false;
- } else if (optionType == DHCP_OPTION_PAD) {
- // The pad option doesn't have a length field. Nothing to do.
- } else {
- int optionLen = packet.get() & 0xFF;
- int expectedLen = 0;
-
- switch(optionType) {
- case DHCP_SUBNET_MASK:
- netMask = readIpAddress(packet);
- expectedLen = 4;
- break;
- case DHCP_ROUTER:
- for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) {
- gateways.add(readIpAddress(packet));
- }
- break;
- case DHCP_DNS_SERVER:
- for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) {
- dnsServers.add(readIpAddress(packet));
- }
- break;
- case DHCP_HOST_NAME:
- expectedLen = optionLen;
- hostName = readAsciiString(packet, optionLen, false);
- break;
- case DHCP_MTU:
- expectedLen = 2;
- mtu = packet.getShort();
- break;
- case DHCP_DOMAIN_NAME:
- expectedLen = optionLen;
- domainName = readAsciiString(packet, optionLen, false);
- break;
- case DHCP_BROADCAST_ADDRESS:
- bcAddr = readIpAddress(packet);
- expectedLen = 4;
- break;
- case DHCP_REQUESTED_IP:
- requestedIp = readIpAddress(packet);
- expectedLen = 4;
- break;
- case DHCP_LEASE_TIME:
- leaseTime = Integer.valueOf(packet.getInt());
- expectedLen = 4;
- break;
- case DHCP_MESSAGE_TYPE:
- dhcpType = packet.get();
- expectedLen = 1;
- break;
- case DHCP_SERVER_IDENTIFIER:
- serverIdentifier = readIpAddress(packet);
- expectedLen = 4;
- break;
- case DHCP_PARAMETER_LIST:
- expectedParams = new byte[optionLen];
- packet.get(expectedParams);
- expectedLen = optionLen;
- break;
- case DHCP_MESSAGE:
- expectedLen = optionLen;
- message = readAsciiString(packet, optionLen, false);
- break;
- case DHCP_MAX_MESSAGE_SIZE:
- expectedLen = 2;
- maxMessageSize = Short.valueOf(packet.getShort());
- break;
- case DHCP_RENEWAL_TIME:
- expectedLen = 4;
- T1 = Integer.valueOf(packet.getInt());
- break;
- case DHCP_REBINDING_TIME:
- expectedLen = 4;
- T2 = Integer.valueOf(packet.getInt());
- break;
- case DHCP_VENDOR_CLASS_ID:
- expectedLen = optionLen;
- // Embedded nulls are safe as this does not get passed to netd.
- vendorId = readAsciiString(packet, optionLen, true);
- break;
- case DHCP_CLIENT_IDENTIFIER: { // Client identifier
- byte[] id = new byte[optionLen];
- packet.get(id);
- expectedLen = optionLen;
- } break;
- case DHCP_VENDOR_INFO:
- expectedLen = optionLen;
- // Embedded nulls are safe as this does not get passed to netd.
- vendorInfo = readAsciiString(packet, optionLen, true);
- break;
- case DHCP_OPTION_OVERLOAD:
- expectedLen = 1;
- optionOverload = packet.get();
- optionOverload &= OPTION_OVERLOAD_BOTH;
- break;
- default:
- // ignore any other parameters
- for (int i = 0; i < optionLen; i++) {
- expectedLen++;
- byte throwaway = packet.get();
- }
- }
-
- if (expectedLen != optionLen) {
- final int errorCode = DhcpErrorEvent.errorCodeWithOption(
- DhcpErrorEvent.DHCP_INVALID_OPTION_LENGTH, optionType);
- throw new ParseException(errorCode,
- "Invalid length %d for option %d, expected %d",
- optionLen, optionType, expectedLen);
- }
- }
- } catch (BufferUnderflowException e) {
- final int errorCode = DhcpErrorEvent.errorCodeWithOption(
- DhcpErrorEvent.BUFFER_UNDERFLOW, optionType);
- throw new ParseException(errorCode, "BufferUnderflowException");
- }
- }
-
- DhcpPacket newPacket;
-
- switch(dhcpType) {
- case (byte) 0xFF:
- throw new ParseException(DhcpErrorEvent.DHCP_NO_MSG_TYPE,
- "No DHCP message type option");
- case DHCP_MESSAGE_TYPE_DISCOVER:
- newPacket = new DhcpDiscoverPacket(transactionId, secs, relayIp, clientMac,
- broadcast, ipSrc);
- break;
- case DHCP_MESSAGE_TYPE_OFFER:
- newPacket = new DhcpOfferPacket(
- transactionId, secs, broadcast, ipSrc, relayIp, clientIp, yourIp, clientMac);
- break;
- case DHCP_MESSAGE_TYPE_REQUEST:
- newPacket = new DhcpRequestPacket(
- transactionId, secs, clientIp, relayIp, clientMac, broadcast);
- break;
- case DHCP_MESSAGE_TYPE_DECLINE:
- newPacket = new DhcpDeclinePacket(
- transactionId, secs, clientIp, yourIp, nextIp, relayIp,
- clientMac);
- break;
- case DHCP_MESSAGE_TYPE_ACK:
- newPacket = new DhcpAckPacket(
- transactionId, secs, broadcast, ipSrc, relayIp, clientIp, yourIp, clientMac);
- break;
- case DHCP_MESSAGE_TYPE_NAK:
- newPacket = new DhcpNakPacket(
- transactionId, secs, relayIp, clientMac, broadcast);
- break;
- case DHCP_MESSAGE_TYPE_RELEASE:
- if (serverIdentifier == null) {
- throw new ParseException(DhcpErrorEvent.MISC_ERROR,
- "DHCPRELEASE without server identifier");
- }
- newPacket = new DhcpReleasePacket(
- transactionId, serverIdentifier, clientIp, relayIp, clientMac);
- break;
- case DHCP_MESSAGE_TYPE_INFORM:
- newPacket = new DhcpInformPacket(
- transactionId, secs, clientIp, yourIp, nextIp, relayIp,
- clientMac);
- break;
- default:
- throw new ParseException(DhcpErrorEvent.DHCP_UNKNOWN_MSG_TYPE,
- "Unimplemented DHCP type %d", dhcpType);
- }
-
- newPacket.mBroadcastAddress = bcAddr;
- newPacket.mClientId = clientId;
- newPacket.mDnsServers = dnsServers;
- newPacket.mDomainName = domainName;
- newPacket.mGateways = gateways;
- newPacket.mHostName = hostName;
- newPacket.mLeaseTime = leaseTime;
- newPacket.mMessage = message;
- newPacket.mMtu = mtu;
- newPacket.mRequestedIp = requestedIp;
- newPacket.mRequestedParams = expectedParams;
- newPacket.mServerIdentifier = serverIdentifier;
- newPacket.mSubnetMask = netMask;
- newPacket.mMaxMessageSize = maxMessageSize;
- newPacket.mT1 = T1;
- newPacket.mT2 = T2;
- newPacket.mVendorId = vendorId;
- newPacket.mVendorInfo = vendorInfo;
- if ((optionOverload & OPTION_OVERLOAD_SNAME) == 0) {
- newPacket.mServerHostName = serverHostName;
- } else {
- newPacket.mServerHostName = "";
- }
- return newPacket;
- }
-
- /**
- * Parse a packet from an array of bytes, stopping at the given length.
- */
- public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType)
- throws ParseException {
- ByteBuffer buffer = ByteBuffer.wrap(packet, 0, length).order(ByteOrder.BIG_ENDIAN);
- try {
- return decodeFullPacket(buffer, pktType);
- } catch (ParseException e) {
- throw e;
- } catch (Exception e) {
- throw new ParseException(DhcpErrorEvent.PARSING_ERROR, e.getMessage());
- }
- }
-
- /**
- * Construct a DhcpResults object from a DHCP reply packet.
- */
- public DhcpResults toDhcpResults() {
- Inet4Address ipAddress = mYourIp;
- if (ipAddress.equals(IPV4_ADDR_ANY)) {
- ipAddress = mClientIp;
- if (ipAddress.equals(IPV4_ADDR_ANY)) {
- return null;
- }
- }
-
- int prefixLength;
- if (mSubnetMask != null) {
- try {
- prefixLength = Inet4AddressUtils.netmaskToPrefixLength(mSubnetMask);
- } catch (IllegalArgumentException e) {
- // Non-contiguous netmask.
- return null;
- }
- } else {
- prefixLength = Inet4AddressUtils.getImplicitNetmask(ipAddress);
- }
-
- DhcpResults results = new DhcpResults();
- try {
- results.ipAddress = new LinkAddress(ipAddress, prefixLength);
- } catch (IllegalArgumentException e) {
- return null;
- }
-
- if (mGateways.size() > 0) {
- results.gateway = mGateways.get(0);
- }
-
- results.dnsServers.addAll(mDnsServers);
- results.domains = mDomainName;
- results.serverAddress = mServerIdentifier;
- results.vendorInfo = mVendorInfo;
- results.leaseDuration = (mLeaseTime != null) ? mLeaseTime : INFINITE_LEASE;
- results.mtu = (mMtu != null && MIN_MTU <= mMtu && mMtu <= MAX_MTU) ? mMtu : 0;
- results.serverHostName = mServerHostName;
-
- return results;
- }
-
- /**
- * Returns the parsed lease time, in milliseconds, or 0 for infinite.
- */
- public long getLeaseTimeMillis() {
- // dhcpcd treats the lack of a lease time option as an infinite lease.
- if (mLeaseTime == null || mLeaseTime == INFINITE_LEASE) {
- return 0;
- } else if (0 <= mLeaseTime && mLeaseTime < MINIMUM_LEASE) {
- return MINIMUM_LEASE * 1000;
- } else {
- return (mLeaseTime & 0xffffffffL) * 1000;
- }
- }
-
- /**
- * Builds a DHCP-DISCOVER packet from the required specified
- * parameters.
- */
- public static ByteBuffer buildDiscoverPacket(int encap, int transactionId,
- short secs, byte[] clientMac, boolean broadcast, byte[] expectedParams) {
- DhcpPacket pkt = new DhcpDiscoverPacket(transactionId, secs, INADDR_ANY /* relayIp */,
- clientMac, broadcast, INADDR_ANY /* srcIp */);
- pkt.mRequestedParams = expectedParams;
- return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
- }
-
- /**
- * Builds a DHCP-OFFER packet from the required specified
- * parameters.
- */
- public static ByteBuffer buildOfferPacket(int encap, int transactionId,
- boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp,
- Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask,
- Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
- Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
- short mtu) {
- DhcpPacket pkt = new DhcpOfferPacket(
- transactionId, (short) 0, broadcast, serverIpAddr, relayIp,
- INADDR_ANY /* clientIp */, yourIp, mac);
- pkt.mGateways = gateways;
- pkt.mDnsServers = dnsServers;
- pkt.mLeaseTime = timeout;
- pkt.mDomainName = domainName;
- pkt.mHostName = hostname;
- pkt.mServerIdentifier = dhcpServerIdentifier;
- pkt.mSubnetMask = netMask;
- pkt.mBroadcastAddress = bcAddr;
- pkt.mMtu = mtu;
- if (metered) {
- pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
- }
- return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
- }
-
- /**
- * Builds a DHCP-ACK packet from the required specified parameters.
- */
- public static ByteBuffer buildAckPacket(int encap, int transactionId,
- boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp,
- Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask,
- Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
- Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
- short mtu) {
- DhcpPacket pkt = new DhcpAckPacket(
- transactionId, (short) 0, broadcast, serverIpAddr, relayIp, requestClientIp, yourIp,
- mac);
- pkt.mGateways = gateways;
- pkt.mDnsServers = dnsServers;
- pkt.mLeaseTime = timeout;
- pkt.mDomainName = domainName;
- pkt.mHostName = hostname;
- pkt.mSubnetMask = netMask;
- pkt.mServerIdentifier = dhcpServerIdentifier;
- pkt.mBroadcastAddress = bcAddr;
- pkt.mMtu = mtu;
- if (metered) {
- pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
- }
- return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
- }
-
- /**
- * Builds a DHCP-NAK packet from the required specified parameters.
- */
- public static ByteBuffer buildNakPacket(int encap, int transactionId, Inet4Address serverIpAddr,
- Inet4Address relayIp, byte[] mac, boolean broadcast, String message) {
- DhcpPacket pkt = new DhcpNakPacket(
- transactionId, (short) 0, relayIp, mac, broadcast);
- pkt.mMessage = message;
- pkt.mServerIdentifier = serverIpAddr;
- return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
- }
-
- /**
- * Builds a DHCP-REQUEST packet from the required specified parameters.
- */
- public static ByteBuffer buildRequestPacket(int encap,
- int transactionId, short secs, Inet4Address clientIp, boolean broadcast,
- byte[] clientMac, Inet4Address requestedIpAddress,
- Inet4Address serverIdentifier, byte[] requestedParams, String hostName) {
- DhcpPacket pkt = new DhcpRequestPacket(transactionId, secs, clientIp,
- INADDR_ANY /* relayIp */, clientMac, broadcast);
- pkt.mRequestedIp = requestedIpAddress;
- pkt.mServerIdentifier = serverIdentifier;
- pkt.mHostName = hostName;
- pkt.mRequestedParams = requestedParams;
- ByteBuffer result = pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
- return result;
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
deleted file mode 100644
index 97d26c7..0000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
+++ /dev/null
@@ -1,88 +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 android.net.dhcp;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.util.FdEventsReader;
-import android.os.Handler;
-import android.system.Os;
-
-import java.io.FileDescriptor;
-import java.net.Inet4Address;
-import java.net.InetSocketAddress;
-
-/**
- * A {@link FdEventsReader} to receive and parse {@link DhcpPacket}.
- * @hide
- */
-abstract class DhcpPacketListener extends FdEventsReader<DhcpPacketListener.Payload> {
- static final class Payload {
- protected final byte[] mBytes = new byte[DhcpPacket.MAX_LENGTH];
- protected Inet4Address mSrcAddr;
- protected int mSrcPort;
- }
-
- DhcpPacketListener(@NonNull Handler handler) {
- super(handler, new Payload());
- }
-
- @Override
- protected int recvBufSize(@NonNull Payload buffer) {
- return buffer.mBytes.length;
- }
-
- @Override
- protected final void handlePacket(@NonNull Payload recvbuf, int length) {
- if (recvbuf.mSrcAddr == null) {
- return;
- }
-
- try {
- final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf.mBytes, length,
- DhcpPacket.ENCAP_BOOTP);
- onReceive(packet, recvbuf.mSrcAddr, recvbuf.mSrcPort);
- } catch (DhcpPacket.ParseException e) {
- logParseError(recvbuf.mBytes, length, e);
- }
- }
-
- @Override
- protected int readPacket(@NonNull FileDescriptor fd, @NonNull Payload packetBuffer)
- throws Exception {
- final InetSocketAddress addr = new InetSocketAddress(0);
- final int read = Os.recvfrom(
- fd, packetBuffer.mBytes, 0, packetBuffer.mBytes.length, 0 /* flags */, addr);
-
- // Buffers with null srcAddr will be dropped in handlePacket()
- packetBuffer.mSrcAddr = inet4AddrOrNull(addr);
- packetBuffer.mSrcPort = addr.getPort();
- return read;
- }
-
- @Nullable
- private static Inet4Address inet4AddrOrNull(@NonNull InetSocketAddress addr) {
- return addr.getAddress() instanceof Inet4Address
- ? (Inet4Address) addr.getAddress()
- : null;
- }
-
- protected abstract void onReceive(@NonNull DhcpPacket packet, @NonNull Inet4Address srcAddr,
- int srcPort);
- protected abstract void logParseError(@NonNull byte[] packet, int length,
- @NonNull DhcpPacket.ParseException e);
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpReleasePacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpReleasePacket.java
deleted file mode 100644
index 3958303..0000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpReleasePacket.java
+++ /dev/null
@@ -1,58 +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 android.net.dhcp;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * Implements DHCP-RELEASE
- */
-class DhcpReleasePacket extends DhcpPacket {
-
- final Inet4Address mClientAddr;
-
- /**
- * Generates a RELEASE packet with the specified parameters.
- */
- public DhcpReleasePacket(int transId, Inet4Address serverId, Inet4Address clientAddr,
- Inet4Address relayIp, byte[] clientMac) {
- super(transId, (short)0, clientAddr, INADDR_ANY /* yourIp */, INADDR_ANY /* nextIp */,
- relayIp, clientMac, false /* broadcast */);
- mServerIdentifier = serverId;
- mClientAddr = clientAddr;
- }
-
-
- @Override
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- fillInPacket(encap, mServerIdentifier /* destIp */, mClientIp /* srcIp */, destUdp, srcUdp,
- result, DHCP_BOOTREPLY, mBroadcast);
- result.flip();
- return result;
- }
-
- @Override
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_RELEASE);
- addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId());
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
- addCommonClientTlvs(buffer);
- addTlvEnd(buffer);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpRequestPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpRequestPacket.java
deleted file mode 100644
index 231d0457..0000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpRequestPacket.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import android.util.Log;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-
-/**
- * This class implements the DHCP-REQUEST packet.
- */
-class DhcpRequestPacket extends DhcpPacket {
- /**
- * Generates a REQUEST packet with the specified parameters.
- */
- DhcpRequestPacket(int transId, short secs, Inet4Address clientIp, Inet4Address relayIp,
- byte[] clientMac, boolean broadcast) {
- super(transId, secs, clientIp, INADDR_ANY, INADDR_ANY, relayIp, clientMac, broadcast);
- }
-
- public String toString() {
- String s = super.toString();
- return s + " REQUEST, desired IP " + mRequestedIp + " from host '"
- + mHostName + "', param list length "
- + (mRequestedParams == null ? 0 : mRequestedParams.length);
- }
-
- /**
- * Fills in a packet with the requested REQUEST attributes.
- */
- public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
-
- fillInPacket(encap, INADDR_BROADCAST, INADDR_ANY, destUdp, srcUdp,
- result, DHCP_BOOTREQUEST, mBroadcast);
- result.flip();
- return result;
- }
-
- /**
- * Adds the optional parameters to the client-generated REQUEST packet.
- */
- void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_REQUEST);
- addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId());
- if (!INADDR_ANY.equals(mRequestedIp)) {
- addTlv(buffer, DHCP_REQUESTED_IP, mRequestedIp);
- }
- if (!INADDR_ANY.equals(mServerIdentifier)) {
- addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
- }
- addCommonClientTlvs(buffer);
- addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
- addTlvEnd(buffer);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
deleted file mode 100644
index b8ab94c..0000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
+++ /dev/null
@@ -1,655 +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 android.net.dhcp;
-
-import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
-import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
-import static android.net.dhcp.DhcpPacket.DHCP_SERVER;
-import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
-import static android.net.dhcp.IDhcpServer.STATUS_INVALID_ARGUMENT;
-import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
-import static android.net.shared.Inet4AddressUtils.getBroadcastAddress;
-import static android.net.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address;
-import static android.system.OsConstants.AF_INET;
-import static android.system.OsConstants.IPPROTO_UDP;
-import static android.system.OsConstants.SOCK_DGRAM;
-import static android.system.OsConstants.SOCK_NONBLOCK;
-import static android.system.OsConstants.SOL_SOCKET;
-import static android.system.OsConstants.SO_BROADCAST;
-import static android.system.OsConstants.SO_REUSEADDR;
-
-import static com.android.internal.util.TrafficStatsConstants.TAG_SYSTEM_DHCP_SERVER;
-import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE;
-import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ALL;
-import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
-import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
-
-import static java.lang.Integer.toUnsignedLong;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.INetworkStackStatusCallback;
-import android.net.MacAddress;
-import android.net.TrafficStats;
-import android.net.util.NetworkStackUtils;
-import android.net.util.SharedLog;
-import android.net.util.SocketUtils;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.text.TextUtils;
-import android.util.Pair;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.HexDump;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-
-/**
- * A DHCPv4 server.
- *
- * <p>This server listens for and responds to packets on a single interface. It considers itself
- * authoritative for all leases on the subnet, which means that DHCP requests for unknown leases of
- * unknown hosts receive a reply instead of being ignored.
- *
- * <p>The server is single-threaded (including send/receive operations): all internal operations are
- * done on the provided {@link Looper}. Public methods are thread-safe and will schedule operations
- * on the looper asynchronously.
- * @hide
- */
-public class DhcpServer extends IDhcpServer.Stub {
- private static final String REPO_TAG = "Repository";
-
- // Lease time to transmit to client instead of a negative time in case a lease expired before
- // the server could send it (if the server process is suspended for example).
- private static final int EXPIRED_FALLBACK_LEASE_TIME_SECS = 120;
-
- private static final int CMD_START_DHCP_SERVER = 1;
- private static final int CMD_STOP_DHCP_SERVER = 2;
- private static final int CMD_UPDATE_PARAMS = 3;
-
- @NonNull
- private final HandlerThread mHandlerThread;
- @NonNull
- private final String mIfName;
- @NonNull
- private final DhcpLeaseRepository mLeaseRepo;
- @NonNull
- private final SharedLog mLog;
- @NonNull
- private final Dependencies mDeps;
- @NonNull
- private final Clock mClock;
-
- @Nullable
- private volatile ServerHandler mHandler;
-
- // Accessed only on the handler thread
- @Nullable
- private DhcpPacketListener mPacketListener;
- @Nullable
- private FileDescriptor mSocket;
- @NonNull
- private DhcpServingParams mServingParams;
-
- /**
- * Clock to be used by DhcpServer to track time for lease expiration.
- *
- * <p>The clock should track time as may be measured by clients obtaining a lease. It does not
- * need to be monotonous across restarts of the server as long as leases are cleared when the
- * server is stopped.
- */
- public static class Clock {
- /**
- * @see SystemClock#elapsedRealtime()
- */
- public long elapsedRealtime() {
- return SystemClock.elapsedRealtime();
- }
- }
-
- /**
- * Dependencies for the DhcpServer. Useful to be mocked in tests.
- */
- public interface Dependencies {
- /**
- * Send a packet to the specified datagram socket.
- *
- * @param fd File descriptor of the socket.
- * @param buffer Data to be sent.
- * @param dst Destination address of the packet.
- */
- void sendPacket(@NonNull FileDescriptor fd, @NonNull ByteBuffer buffer,
- @NonNull InetAddress dst) throws ErrnoException, IOException;
-
- /**
- * Create a DhcpLeaseRepository for the server.
- * @param servingParams Parameters used to serve DHCP requests.
- * @param log Log to be used by the repository.
- * @param clock Clock that the repository must use to track time.
- */
- DhcpLeaseRepository makeLeaseRepository(@NonNull DhcpServingParams servingParams,
- @NonNull SharedLog log, @NonNull Clock clock);
-
- /**
- * Create a packet listener that will send packets to be processed.
- */
- DhcpPacketListener makePacketListener();
-
- /**
- * Create a clock that the server will use to track time.
- */
- Clock makeClock();
-
- /**
- * Add an entry to the ARP cache table.
- * @param fd Datagram socket file descriptor that must use the new entry.
- */
- void addArpEntry(@NonNull Inet4Address ipv4Addr, @NonNull MacAddress ethAddr,
- @NonNull String ifname, @NonNull FileDescriptor fd) throws IOException;
-
- /**
- * Verify that the caller is allowed to call public methods on DhcpServer.
- * @throws SecurityException The caller is not allowed to call public methods on DhcpServer.
- */
- void checkCaller() throws SecurityException;
- }
-
- private class DependenciesImpl implements Dependencies {
- @Override
- public void sendPacket(@NonNull FileDescriptor fd, @NonNull ByteBuffer buffer,
- @NonNull InetAddress dst) throws ErrnoException, IOException {
- Os.sendto(fd, buffer, 0, dst, DhcpPacket.DHCP_CLIENT);
- }
-
- @Override
- public DhcpLeaseRepository makeLeaseRepository(@NonNull DhcpServingParams servingParams,
- @NonNull SharedLog log, @NonNull Clock clock) {
- return new DhcpLeaseRepository(
- DhcpServingParams.makeIpPrefix(servingParams.serverAddr),
- servingParams.excludedAddrs,
- servingParams.dhcpLeaseTimeSecs * 1000, log.forSubComponent(REPO_TAG), clock);
- }
-
- @Override
- public DhcpPacketListener makePacketListener() {
- return new PacketListener();
- }
-
- @Override
- public Clock makeClock() {
- return new Clock();
- }
-
- @Override
- public void addArpEntry(@NonNull Inet4Address ipv4Addr, @NonNull MacAddress ethAddr,
- @NonNull String ifname, @NonNull FileDescriptor fd) throws IOException {
- NetworkStackUtils.addArpEntry(ipv4Addr, ethAddr, ifname, fd);
- }
-
- @Override
- public void checkCaller() {
- checkNetworkStackCallingPermission();
- }
- }
-
- private static class MalformedPacketException extends Exception {
- MalformedPacketException(String message, Throwable t) {
- super(message, t);
- }
- }
-
- public DhcpServer(@NonNull String ifName,
- @NonNull DhcpServingParams params, @NonNull SharedLog log) {
- this(new HandlerThread(DhcpServer.class.getSimpleName() + "." + ifName),
- ifName, params, log, null);
- }
-
- @VisibleForTesting
- DhcpServer(@NonNull HandlerThread handlerThread, @NonNull String ifName,
- @NonNull DhcpServingParams params, @NonNull SharedLog log,
- @Nullable Dependencies deps) {
- if (deps == null) {
- deps = new DependenciesImpl();
- }
- mHandlerThread = handlerThread;
- mIfName = ifName;
- mServingParams = params;
- mLog = log;
- mDeps = deps;
- mClock = deps.makeClock();
- mLeaseRepo = deps.makeLeaseRepository(mServingParams, mLog, mClock);
- }
-
- /**
- * Start listening for and responding to packets.
- *
- * <p>It is not legal to call this method more than once; in particular the server cannot be
- * restarted after being stopped.
- */
- @Override
- public void start(@Nullable INetworkStackStatusCallback cb) {
- mDeps.checkCaller();
- mHandlerThread.start();
- mHandler = new ServerHandler(mHandlerThread.getLooper());
- sendMessage(CMD_START_DHCP_SERVER, cb);
- }
-
- /**
- * Update serving parameters. All subsequently received requests will be handled with the new
- * parameters, and current leases that are incompatible with the new parameters are dropped.
- */
- @Override
- public void updateParams(@Nullable DhcpServingParamsParcel params,
- @Nullable INetworkStackStatusCallback cb) throws RemoteException {
- mDeps.checkCaller();
- final DhcpServingParams parsedParams;
- try {
- // throws InvalidParameterException with null params
- parsedParams = DhcpServingParams.fromParcelableObject(params);
- } catch (DhcpServingParams.InvalidParameterException e) {
- mLog.e("Invalid parameters sent to DhcpServer", e);
- if (cb != null) {
- cb.onStatusAvailable(STATUS_INVALID_ARGUMENT);
- }
- return;
- }
- sendMessage(CMD_UPDATE_PARAMS, new Pair<>(parsedParams, cb));
- }
-
- /**
- * Stop listening for packets.
- *
- * <p>As the server is stopped asynchronously, some packets may still be processed shortly after
- * calling this method.
- */
- @Override
- public void stop(@Nullable INetworkStackStatusCallback cb) {
- mDeps.checkCaller();
- sendMessage(CMD_STOP_DHCP_SERVER, cb);
- }
-
- private void sendMessage(int what, @Nullable Object obj) {
- if (mHandler == null) {
- mLog.e("Attempting to send a command to stopped DhcpServer: " + what);
- return;
- }
- mHandler.sendMessage(mHandler.obtainMessage(what, obj));
- }
-
- private class ServerHandler extends Handler {
- ServerHandler(@NonNull Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(@NonNull Message msg) {
- final INetworkStackStatusCallback cb;
- switch (msg.what) {
- case CMD_UPDATE_PARAMS:
- final Pair<DhcpServingParams, INetworkStackStatusCallback> pair =
- (Pair<DhcpServingParams, INetworkStackStatusCallback>) msg.obj;
- final DhcpServingParams params = pair.first;
- mServingParams = params;
- mLeaseRepo.updateParams(
- DhcpServingParams.makeIpPrefix(mServingParams.serverAddr),
- params.excludedAddrs,
- params.dhcpLeaseTimeSecs);
-
- cb = pair.second;
- break;
- case CMD_START_DHCP_SERVER:
- mPacketListener = mDeps.makePacketListener();
- mPacketListener.start();
- cb = (INetworkStackStatusCallback) msg.obj;
- break;
- case CMD_STOP_DHCP_SERVER:
- if (mPacketListener != null) {
- mPacketListener.stop();
- mPacketListener = null;
- }
- mHandlerThread.quitSafely();
- cb = (INetworkStackStatusCallback) msg.obj;
- break;
- default:
- return;
- }
- if (cb != null) {
- try {
- cb.onStatusAvailable(STATUS_SUCCESS);
- } catch (RemoteException e) {
- mLog.e("Could not send status back to caller", e);
- }
- }
- }
- }
-
- @VisibleForTesting
- void processPacket(@NonNull DhcpPacket packet, int srcPort) {
- final String packetType = packet.getClass().getSimpleName();
- if (srcPort != DHCP_CLIENT) {
- mLog.logf("Ignored packet of type %s sent from client port %d", packetType, srcPort);
- return;
- }
-
- mLog.log("Received packet of type " + packetType);
- final Inet4Address sid = packet.mServerIdentifier;
- if (sid != null && !sid.equals(mServingParams.serverAddr.getAddress())) {
- mLog.log("Packet ignored due to wrong server identifier: " + sid);
- return;
- }
-
- try {
- if (packet instanceof DhcpDiscoverPacket) {
- processDiscover((DhcpDiscoverPacket) packet);
- } else if (packet instanceof DhcpRequestPacket) {
- processRequest((DhcpRequestPacket) packet);
- } else if (packet instanceof DhcpReleasePacket) {
- processRelease((DhcpReleasePacket) packet);
- } else {
- mLog.e("Unknown packet type: " + packet.getClass().getSimpleName());
- }
- } catch (MalformedPacketException e) {
- // Not an internal error: only logging exception message, not stacktrace
- mLog.e("Ignored malformed packet: " + e.getMessage());
- }
- }
-
- private void logIgnoredPacketInvalidSubnet(DhcpLeaseRepository.InvalidSubnetException e) {
- // Not an internal error: only logging exception message, not stacktrace
- mLog.e("Ignored packet from invalid subnet: " + e.getMessage());
- }
-
- private void processDiscover(@NonNull DhcpDiscoverPacket packet)
- throws MalformedPacketException {
- final DhcpLease lease;
- final MacAddress clientMac = getMacAddr(packet);
- try {
- lease = mLeaseRepo.getOffer(packet.getExplicitClientIdOrNull(), clientMac,
- packet.mRelayIp, packet.mRequestedIp, packet.mHostName);
- } catch (DhcpLeaseRepository.OutOfAddressesException e) {
- transmitNak(packet, "Out of addresses to offer");
- return;
- } catch (DhcpLeaseRepository.InvalidSubnetException e) {
- logIgnoredPacketInvalidSubnet(e);
- return;
- }
-
- transmitOffer(packet, lease, clientMac);
- }
-
- private void processRequest(@NonNull DhcpRequestPacket packet) throws MalformedPacketException {
- // If set, packet SID matches with this server's ID as checked in processPacket().
- final boolean sidSet = packet.mServerIdentifier != null;
- final DhcpLease lease;
- final MacAddress clientMac = getMacAddr(packet);
- try {
- lease = mLeaseRepo.requestLease(packet.getExplicitClientIdOrNull(), clientMac,
- packet.mClientIp, packet.mRelayIp, packet.mRequestedIp, sidSet,
- packet.mHostName);
- } catch (DhcpLeaseRepository.InvalidAddressException e) {
- transmitNak(packet, "Invalid requested address");
- return;
- } catch (DhcpLeaseRepository.InvalidSubnetException e) {
- logIgnoredPacketInvalidSubnet(e);
- return;
- }
-
- transmitAck(packet, lease, clientMac);
- }
-
- private void processRelease(@NonNull DhcpReleasePacket packet)
- throws MalformedPacketException {
- final byte[] clientId = packet.getExplicitClientIdOrNull();
- final MacAddress macAddr = getMacAddr(packet);
- // Don't care about success (there is no ACK/NAK); logging is already done in the repository
- mLeaseRepo.releaseLease(clientId, macAddr, packet.mClientIp);
- }
-
- private Inet4Address getAckOrOfferDst(@NonNull DhcpPacket request, @NonNull DhcpLease lease,
- boolean broadcastFlag) {
- // Unless relayed or broadcast, send to client IP if already configured on the client, or to
- // the lease address if the client has no configured address
- if (!isEmpty(request.mRelayIp)) {
- return request.mRelayIp;
- } else if (broadcastFlag) {
- return IPV4_ADDR_ALL;
- } else if (!isEmpty(request.mClientIp)) {
- return request.mClientIp;
- } else {
- return lease.getNetAddr();
- }
- }
-
- /**
- * Determine whether the broadcast flag should be set in the BOOTP packet flags. This does not
- * apply to NAK responses, which should always have it set.
- */
- private static boolean getBroadcastFlag(@NonNull DhcpPacket request, @NonNull DhcpLease lease) {
- // No broadcast flag if the client already has a configured IP to unicast to. RFC2131 #4.1
- // has some contradictions regarding broadcast behavior if a client already has an IP
- // configured and sends a request with both ciaddr (renew/rebind) and the broadcast flag
- // set. Sending a unicast response to ciaddr matches previous behavior and is more
- // efficient.
- // If the client has no configured IP, broadcast if requested by the client or if the lease
- // address cannot be used to send a unicast reply either.
- return isEmpty(request.mClientIp) && (request.mBroadcast || isEmpty(lease.getNetAddr()));
- }
-
- /**
- * Get the hostname from a lease if non-empty and requested in the incoming request.
- * @param request The incoming request.
- * @return The hostname, or null if not requested or empty.
- */
- @Nullable
- private static String getHostnameIfRequested(@NonNull DhcpPacket request,
- @NonNull DhcpLease lease) {
- return request.hasRequestedParam(DHCP_HOST_NAME) && !TextUtils.isEmpty(lease.getHostname())
- ? lease.getHostname()
- : null;
- }
-
- private boolean transmitOffer(@NonNull DhcpPacket request, @NonNull DhcpLease lease,
- @NonNull MacAddress clientMac) {
- final boolean broadcastFlag = getBroadcastFlag(request, lease);
- final int timeout = getLeaseTimeout(lease);
- final Inet4Address prefixMask =
- getPrefixMaskAsInet4Address(mServingParams.serverAddr.getPrefixLength());
- final Inet4Address broadcastAddr = getBroadcastAddress(
- mServingParams.getServerInet4Addr(), mServingParams.serverAddr.getPrefixLength());
- final String hostname = getHostnameIfRequested(request, lease);
- final ByteBuffer offerPacket = DhcpPacket.buildOfferPacket(
- ENCAP_BOOTP, request.mTransId, broadcastFlag, mServingParams.getServerInet4Addr(),
- request.mRelayIp, lease.getNetAddr(), request.mClientMac, timeout, prefixMask,
- broadcastAddr, new ArrayList<>(mServingParams.defaultRouters),
- new ArrayList<>(mServingParams.dnsServers),
- mServingParams.getServerInet4Addr(), null /* domainName */, hostname,
- mServingParams.metered, (short) mServingParams.linkMtu);
-
- return transmitOfferOrAckPacket(offerPacket, request, lease, clientMac, broadcastFlag);
- }
-
- private boolean transmitAck(@NonNull DhcpPacket request, @NonNull DhcpLease lease,
- @NonNull MacAddress clientMac) {
- // TODO: replace DhcpPacket's build methods with real builders and use common code with
- // transmitOffer above
- final boolean broadcastFlag = getBroadcastFlag(request, lease);
- final int timeout = getLeaseTimeout(lease);
- final String hostname = getHostnameIfRequested(request, lease);
- final ByteBuffer ackPacket = DhcpPacket.buildAckPacket(ENCAP_BOOTP, request.mTransId,
- broadcastFlag, mServingParams.getServerInet4Addr(), request.mRelayIp,
- lease.getNetAddr(), request.mClientIp, request.mClientMac, timeout,
- mServingParams.getPrefixMaskAsAddress(), mServingParams.getBroadcastAddress(),
- new ArrayList<>(mServingParams.defaultRouters),
- new ArrayList<>(mServingParams.dnsServers),
- mServingParams.getServerInet4Addr(), null /* domainName */, hostname,
- mServingParams.metered, (short) mServingParams.linkMtu);
-
- return transmitOfferOrAckPacket(ackPacket, request, lease, clientMac, broadcastFlag);
- }
-
- private boolean transmitNak(DhcpPacket request, String message) {
- mLog.w("Transmitting NAK: " + message);
- // Always set broadcast flag for NAK: client may not have a correct IP
- final ByteBuffer nakPacket = DhcpPacket.buildNakPacket(
- ENCAP_BOOTP, request.mTransId, mServingParams.getServerInet4Addr(),
- request.mRelayIp, request.mClientMac, true /* broadcast */, message);
-
- final Inet4Address dst = isEmpty(request.mRelayIp)
- ? IPV4_ADDR_ALL
- : request.mRelayIp;
- return transmitPacket(nakPacket, DhcpNakPacket.class.getSimpleName(), dst);
- }
-
- private boolean transmitOfferOrAckPacket(@NonNull ByteBuffer buf, @NonNull DhcpPacket request,
- @NonNull DhcpLease lease, @NonNull MacAddress clientMac, boolean broadcastFlag) {
- mLog.logf("Transmitting %s with lease %s", request.getClass().getSimpleName(), lease);
- // Client may not yet respond to ARP for the lease address, which may be the destination
- // address. Add an entry to the ARP cache to save future ARP probes and make sure the
- // packet reaches its destination.
- if (!addArpEntry(clientMac, lease.getNetAddr())) {
- // Logging for error already done
- return false;
- }
- final Inet4Address dst = getAckOrOfferDst(request, lease, broadcastFlag);
- return transmitPacket(buf, request.getClass().getSimpleName(), dst);
- }
-
- private boolean transmitPacket(@NonNull ByteBuffer buf, @NonNull String packetTypeTag,
- @NonNull Inet4Address dst) {
- try {
- mDeps.sendPacket(mSocket, buf, dst);
- } catch (ErrnoException | IOException e) {
- mLog.e("Can't send packet " + packetTypeTag, e);
- return false;
- }
- return true;
- }
-
- private boolean addArpEntry(@NonNull MacAddress macAddr, @NonNull Inet4Address inetAddr) {
- try {
- mDeps.addArpEntry(inetAddr, macAddr, mIfName, mSocket);
- return true;
- } catch (IOException e) {
- mLog.e("Error adding client to ARP table", e);
- return false;
- }
- }
-
- /**
- * Get the remaining lease time in seconds, starting from {@link Clock#elapsedRealtime()}.
- *
- * <p>This is an unsigned 32-bit integer, so it cannot be read as a standard (signed) Java int.
- * The return value is only intended to be used to populate the lease time field in a DHCP
- * response, considering that lease time is an unsigned 32-bit integer field in DHCP packets.
- *
- * <p>Lease expiration times are tracked internally with millisecond precision: this method
- * returns a rounded down value.
- */
- private int getLeaseTimeout(@NonNull DhcpLease lease) {
- final long remainingTimeSecs = (lease.getExpTime() - mClock.elapsedRealtime()) / 1000;
- if (remainingTimeSecs < 0) {
- mLog.e("Processing expired lease " + lease);
- return EXPIRED_FALLBACK_LEASE_TIME_SECS;
- }
-
- if (remainingTimeSecs >= toUnsignedLong(INFINITE_LEASE)) {
- return INFINITE_LEASE;
- }
-
- return (int) remainingTimeSecs;
- }
-
- /**
- * Get the client MAC address from a packet.
- *
- * @throws MalformedPacketException The address in the packet uses an unsupported format.
- */
- @NonNull
- private MacAddress getMacAddr(@NonNull DhcpPacket packet) throws MalformedPacketException {
- try {
- return MacAddress.fromBytes(packet.getClientMac());
- } catch (IllegalArgumentException e) {
- final String message = "Invalid MAC address in packet: "
- + HexDump.dumpHexString(packet.getClientMac());
- throw new MalformedPacketException(message, e);
- }
- }
-
- private static boolean isEmpty(@Nullable Inet4Address address) {
- return address == null || IPV4_ADDR_ANY.equals(address);
- }
-
- private class PacketListener extends DhcpPacketListener {
- PacketListener() {
- super(mHandler);
- }
-
- @Override
- protected void onReceive(@NonNull DhcpPacket packet, @NonNull Inet4Address srcAddr,
- int srcPort) {
- processPacket(packet, srcPort);
- }
-
- @Override
- protected void logError(@NonNull String msg, Exception e) {
- mLog.e("Error receiving packet: " + msg, e);
- }
-
- @Override
- protected void logParseError(@NonNull byte[] packet, int length,
- @NonNull DhcpPacket.ParseException e) {
- mLog.e("Error parsing packet", e);
- }
-
- @Override
- protected FileDescriptor createFd() {
- // TODO: have and use an API to set a socket tag without going through the thread tag
- final int oldTag = TrafficStats.getAndSetThreadStatsTag(TAG_SYSTEM_DHCP_SERVER);
- try {
- mSocket = Os.socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
- SocketUtils.bindSocketToInterface(mSocket, mIfName);
- Os.setsockoptInt(mSocket, SOL_SOCKET, SO_REUSEADDR, 1);
- Os.setsockoptInt(mSocket, SOL_SOCKET, SO_BROADCAST, 1);
- Os.bind(mSocket, IPV4_ADDR_ANY, DHCP_SERVER);
-
- return mSocket;
- } catch (IOException | ErrnoException e) {
- mLog.e("Error creating UDP socket", e);
- DhcpServer.this.stop(null);
- return null;
- } finally {
- TrafficStats.setThreadStatsTag(oldTag);
- }
- }
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
-}
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
deleted file mode 100644
index 230b693..0000000
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
+++ /dev/null
@@ -1,377 +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 android.net.dhcp;
-
-import static android.net.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address;
-import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
-
-import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE;
-import static com.android.server.util.NetworkStackConstants.IPV4_MAX_MTU;
-import static com.android.server.util.NetworkStackConstants.IPV4_MIN_MTU;
-
-import static java.lang.Integer.toUnsignedLong;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.shared.Inet4AddressUtils;
-import android.util.ArraySet;
-
-import java.net.Inet4Address;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Parameters used by the DhcpServer to serve requests.
- *
- * <p>Instances are immutable. Use {@link DhcpServingParams.Builder} to instantiate.
- * @hide
- */
-public class DhcpServingParams {
- public static final int MTU_UNSET = 0;
- public static final int MIN_PREFIX_LENGTH = 16;
- public static final int MAX_PREFIX_LENGTH = 30;
-
- /** Server inet address and prefix to serve */
- @NonNull
- public final LinkAddress serverAddr;
-
- /**
- * Default routers to be advertised to DHCP clients. May be empty.
- * This set is provided by {@link DhcpServingParams.Builder} and is immutable.
- */
- @NonNull
- public final Set<Inet4Address> defaultRouters;
-
- /**
- * DNS servers to be advertised to DHCP clients. May be empty.
- * This set is provided by {@link DhcpServingParams.Builder} and is immutable.
- */
- @NonNull
- public final Set<Inet4Address> dnsServers;
-
- /**
- * Excluded addresses that the DHCP server is not allowed to assign to clients.
- * This set is provided by {@link DhcpServingParams.Builder} and is immutable.
- */
- @NonNull
- public final Set<Inet4Address> excludedAddrs;
-
- // DHCP uses uint32. Use long for clearer code, and check range when building.
- public final long dhcpLeaseTimeSecs;
- public final int linkMtu;
-
- /**
- * Indicates whether the DHCP server should send the ANDROID_METERED vendor-specific option.
- */
- public final boolean metered;
-
- /**
- * Checked exception thrown when some parameters used to build {@link DhcpServingParams} are
- * missing or invalid.
- */
- public static class InvalidParameterException extends Exception {
- public InvalidParameterException(String message) {
- super(message);
- }
- }
-
- private DhcpServingParams(@NonNull LinkAddress serverAddr,
- @NonNull Set<Inet4Address> defaultRouters,
- @NonNull Set<Inet4Address> dnsServers, @NonNull Set<Inet4Address> excludedAddrs,
- long dhcpLeaseTimeSecs, int linkMtu, boolean metered) {
- this.serverAddr = serverAddr;
- this.defaultRouters = defaultRouters;
- this.dnsServers = dnsServers;
- this.excludedAddrs = excludedAddrs;
- this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
- this.linkMtu = linkMtu;
- this.metered = metered;
- }
-
- /**
- * Create parameters from a stable AIDL-compatible parcel.
- * @throws InvalidParameterException The parameters parcelable is null or invalid.
- */
- public static DhcpServingParams fromParcelableObject(@Nullable DhcpServingParamsParcel parcel)
- throws InvalidParameterException {
- if (parcel == null) {
- throw new InvalidParameterException("Null serving parameters");
- }
- final LinkAddress serverAddr = new LinkAddress(
- intToInet4AddressHTH(parcel.serverAddr),
- parcel.serverAddrPrefixLength);
- return new Builder()
- .setServerAddr(serverAddr)
- .setDefaultRouters(toInet4AddressSet(parcel.defaultRouters))
- .setDnsServers(toInet4AddressSet(parcel.dnsServers))
- .setExcludedAddrs(toInet4AddressSet(parcel.excludedAddrs))
- .setDhcpLeaseTimeSecs(parcel.dhcpLeaseTimeSecs)
- .setLinkMtu(parcel.linkMtu)
- .setMetered(parcel.metered)
- .build();
- }
-
- private static Set<Inet4Address> toInet4AddressSet(@Nullable int[] addrs) {
- if (addrs == null) {
- return new HashSet<>(0);
- }
-
- final HashSet<Inet4Address> res = new HashSet<>();
- for (int addr : addrs) {
- res.add(intToInet4AddressHTH(addr));
- }
- return res;
- }
-
- @NonNull
- public Inet4Address getServerInet4Addr() {
- return (Inet4Address) serverAddr.getAddress();
- }
-
- /**
- * Get the served prefix mask as an IPv4 address.
- *
- * <p>For example, if the served prefix is 192.168.42.0/24, this will return 255.255.255.0.
- */
- @NonNull
- public Inet4Address getPrefixMaskAsAddress() {
- return getPrefixMaskAsInet4Address(serverAddr.getPrefixLength());
- }
-
- /**
- * Get the server broadcast address.
- *
- * <p>For example, if the server {@link LinkAddress} is 192.168.42.1/24, this will return
- * 192.168.42.255.
- */
- @NonNull
- public Inet4Address getBroadcastAddress() {
- return Inet4AddressUtils.getBroadcastAddress(
- getServerInet4Addr(), serverAddr.getPrefixLength());
- }
-
- /**
- * Utility class to create new instances of {@link DhcpServingParams} while checking validity
- * of the parameters.
- */
- public static class Builder {
- private LinkAddress mServerAddr;
- private Set<Inet4Address> mDefaultRouters;
- private Set<Inet4Address> mDnsServers;
- private Set<Inet4Address> mExcludedAddrs;
- private long mDhcpLeaseTimeSecs;
- private int mLinkMtu = MTU_UNSET;
- private boolean mMetered;
-
- /**
- * Set the server address and served prefix for the DHCP server.
- *
- * <p>This parameter is required.
- */
- public Builder setServerAddr(@NonNull LinkAddress serverAddr) {
- this.mServerAddr = serverAddr;
- return this;
- }
-
- /**
- * Set the default routers to be advertised to DHCP clients.
- *
- * <p>Each router must be inside the served prefix. This may be an empty set, but it must
- * always be set explicitly before building the {@link DhcpServingParams}.
- */
- public Builder setDefaultRouters(@NonNull Set<Inet4Address> defaultRouters) {
- this.mDefaultRouters = defaultRouters;
- return this;
- }
-
- /**
- * Set the default routers to be advertised to DHCP clients.
- *
- * <p>Each router must be inside the served prefix. This may be an empty list of routers,
- * but it must always be set explicitly before building the {@link DhcpServingParams}.
- */
- public Builder setDefaultRouters(@NonNull Inet4Address... defaultRouters) {
- return setDefaultRouters(makeArraySet(defaultRouters));
- }
-
- /**
- * Convenience method to build the parameters with no default router.
- *
- * <p>Equivalent to calling {@link #setDefaultRouters(Inet4Address...)} with no address.
- */
- public Builder withNoDefaultRouter() {
- return setDefaultRouters();
- }
-
- /**
- * Set the DNS servers to be advertised to DHCP clients.
- *
- * <p>This may be an empty set, but it must always be set explicitly before building the
- * {@link DhcpServingParams}.
- */
- public Builder setDnsServers(@NonNull Set<Inet4Address> dnsServers) {
- this.mDnsServers = dnsServers;
- return this;
- }
-
- /**
- * Set the DNS servers to be advertised to DHCP clients.
- *
- * <p>This may be an empty list of servers, but it must always be set explicitly before
- * building the {@link DhcpServingParams}.
- */
- public Builder setDnsServers(@NonNull Inet4Address... dnsServers) {
- return setDnsServers(makeArraySet(dnsServers));
- }
-
- /**
- * Convenience method to build the parameters with no DNS server.
- *
- * <p>Equivalent to calling {@link #setDnsServers(Inet4Address...)} with no address.
- */
- public Builder withNoDnsServer() {
- return setDnsServers();
- }
-
- /**
- * Set excluded addresses that the DHCP server is not allowed to assign to clients.
- *
- * <p>This parameter is optional. DNS servers and default routers are always excluded
- * and do not need to be set here.
- */
- public Builder setExcludedAddrs(@NonNull Set<Inet4Address> excludedAddrs) {
- this.mExcludedAddrs = excludedAddrs;
- return this;
- }
-
- /**
- * Set excluded addresses that the DHCP server is not allowed to assign to clients.
- *
- * <p>This parameter is optional. DNS servers and default routers are always excluded
- * and do not need to be set here.
- */
- public Builder setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) {
- return setExcludedAddrs(makeArraySet(excludedAddrs));
- }
-
- /**
- * Set the lease time for leases assigned by the DHCP server.
- *
- * <p>This parameter is required.
- */
- public Builder setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) {
- this.mDhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
- return this;
- }
-
- /**
- * Set the link MTU to be advertised to DHCP clients.
- *
- * <p>If set to {@link #MTU_UNSET}, no MTU will be advertised to clients. This parameter
- * is optional and defaults to {@link #MTU_UNSET}.
- */
- public Builder setLinkMtu(int linkMtu) {
- this.mLinkMtu = linkMtu;
- return this;
- }
-
- /**
- * Set whether the DHCP server should send the ANDROID_METERED vendor-specific option.
- *
- * <p>If not set, the default value is false.
- */
- public Builder setMetered(boolean metered) {
- this.mMetered = metered;
- return this;
- }
-
- /**
- * Create a new {@link DhcpServingParams} instance based on parameters set in the builder.
- *
- * <p>This method has no side-effects. If it does not throw, a valid
- * {@link DhcpServingParams} is returned.
- * @return The constructed parameters.
- * @throws InvalidParameterException At least one parameter is missing or invalid.
- */
- @NonNull
- public DhcpServingParams build() throws InvalidParameterException {
- if (mServerAddr == null) {
- throw new InvalidParameterException("Missing serverAddr");
- }
- if (mDefaultRouters == null) {
- throw new InvalidParameterException("Missing defaultRouters");
- }
- if (mDnsServers == null) {
- // Empty set is OK, but enforce explicitly setting it
- throw new InvalidParameterException("Missing dnsServers");
- }
- if (mDhcpLeaseTimeSecs <= 0 || mDhcpLeaseTimeSecs > toUnsignedLong(INFINITE_LEASE)) {
- throw new InvalidParameterException("Invalid lease time: " + mDhcpLeaseTimeSecs);
- }
- if (mLinkMtu != MTU_UNSET && (mLinkMtu < IPV4_MIN_MTU || mLinkMtu > IPV4_MAX_MTU)) {
- throw new InvalidParameterException("Invalid link MTU: " + mLinkMtu);
- }
- if (!mServerAddr.isIpv4()) {
- throw new InvalidParameterException("serverAddr must be IPv4");
- }
- if (mServerAddr.getPrefixLength() < MIN_PREFIX_LENGTH
- || mServerAddr.getPrefixLength() > MAX_PREFIX_LENGTH) {
- throw new InvalidParameterException("Prefix length is not in supported range");
- }
-
- final IpPrefix prefix = makeIpPrefix(mServerAddr);
- for (Inet4Address addr : mDefaultRouters) {
- if (!prefix.contains(addr)) {
- throw new InvalidParameterException(String.format(
- "Default router %s is not in server prefix %s", addr, mServerAddr));
- }
- }
-
- final Set<Inet4Address> excl = new HashSet<>();
- if (mExcludedAddrs != null) {
- excl.addAll(mExcludedAddrs);
- }
- excl.add((Inet4Address) mServerAddr.getAddress());
- excl.addAll(mDefaultRouters);
- excl.addAll(mDnsServers);
-
- return new DhcpServingParams(mServerAddr,
- Collections.unmodifiableSet(new HashSet<>(mDefaultRouters)),
- Collections.unmodifiableSet(new HashSet<>(mDnsServers)),
- Collections.unmodifiableSet(excl),
- mDhcpLeaseTimeSecs, mLinkMtu, mMetered);
- }
- }
-
- /**
- * Utility method to create an IpPrefix with the address and prefix length of a LinkAddress.
- */
- @NonNull
- static IpPrefix makeIpPrefix(@NonNull LinkAddress addr) {
- return new IpPrefix(addr.getAddress(), addr.getPrefixLength());
- }
-
- private static <T> ArraySet<T> makeArraySet(T[] elements) {
- final ArraySet<T> set = new ArraySet<>(elements.length);
- set.addAll(Arrays.asList(elements));
- return set;
- }
-}
diff --git a/packages/NetworkStack/src/android/net/ip/ConnectivityPacketTracker.java b/packages/NetworkStack/src/android/net/ip/ConnectivityPacketTracker.java
deleted file mode 100644
index eb49218..0000000
--- a/packages/NetworkStack/src/android/net/ip/ConnectivityPacketTracker.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import static android.net.util.SocketUtils.makePacketSocketAddress;
-import static android.system.OsConstants.AF_PACKET;
-import static android.system.OsConstants.ARPHRD_ETHER;
-import static android.system.OsConstants.ETH_P_ALL;
-import static android.system.OsConstants.SOCK_NONBLOCK;
-import static android.system.OsConstants.SOCK_RAW;
-
-import android.net.util.ConnectivityPacketSummary;
-import android.net.util.InterfaceParams;
-import android.net.util.NetworkStackUtils;
-import android.net.util.PacketReader;
-import android.os.Handler;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.text.TextUtils;
-import android.util.LocalLog;
-import android.util.Log;
-
-import com.android.internal.util.HexDump;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-
-
-/**
- * Critical connectivity packet tracking daemon.
- *
- * Tracks ARP, DHCPv4, and IPv6 RS/RA/NS/NA packets.
- *
- * This class's constructor, start() and stop() methods must only be called
- * from the same thread on which the passed in |log| is accessed.
- *
- * Log lines include a hexdump of the packet, which can be decoded via:
- *
- * echo -n H3XSTR1NG | sed -e 's/\([0-9A-F][0-9A-F]\)/\1 /g' -e 's/^/000000 /'
- * | text2pcap - -
- * | tcpdump -n -vv -e -r -
- *
- * @hide
- */
-public class ConnectivityPacketTracker {
- private static final String TAG = ConnectivityPacketTracker.class.getSimpleName();
- private static final boolean DBG = false;
- private static final String MARK_START = "--- START ---";
- private static final String MARK_STOP = "--- STOP ---";
- private static final String MARK_NAMED_START = "--- START (%s) ---";
- private static final String MARK_NAMED_STOP = "--- STOP (%s) ---";
-
- private final String mTag;
- private final LocalLog mLog;
- private final PacketReader mPacketListener;
- private boolean mRunning;
- private String mDisplayName;
-
- public ConnectivityPacketTracker(Handler h, InterfaceParams ifParams, LocalLog log) {
- if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams");
-
- mTag = TAG + "." + ifParams.name;
- mLog = log;
- mPacketListener = new PacketListener(h, ifParams);
- }
-
- public void start(String displayName) {
- mRunning = true;
- mDisplayName = displayName;
- mPacketListener.start();
- }
-
- public void stop() {
- mPacketListener.stop();
- mRunning = false;
- mDisplayName = null;
- }
-
- private final class PacketListener extends PacketReader {
- private final InterfaceParams mInterface;
-
- PacketListener(Handler h, InterfaceParams ifParams) {
- super(h, ifParams.defaultMtu);
- mInterface = ifParams;
- }
-
- @Override
- protected FileDescriptor createFd() {
- FileDescriptor s = null;
- try {
- s = Os.socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, 0);
- NetworkStackUtils.attachControlPacketFilter(s, ARPHRD_ETHER);
- Os.bind(s, makePacketSocketAddress((short) ETH_P_ALL, mInterface.index));
- } catch (ErrnoException | IOException e) {
- logError("Failed to create packet tracking socket: ", e);
- closeFd(s);
- return null;
- }
- return s;
- }
-
- @Override
- protected void handlePacket(byte[] recvbuf, int length) {
- final String summary = ConnectivityPacketSummary.summarize(
- mInterface.macAddr, recvbuf, length);
- if (summary == null) return;
-
- if (DBG) Log.d(mTag, summary);
- addLogEntry(summary + "\n[" + HexDump.toHexString(recvbuf, 0, length) + "]");
- }
-
- @Override
- protected void onStart() {
- final String msg = TextUtils.isEmpty(mDisplayName)
- ? MARK_START
- : String.format(MARK_NAMED_START, mDisplayName);
- mLog.log(msg);
- }
-
- @Override
- protected void onStop() {
- String msg = TextUtils.isEmpty(mDisplayName)
- ? MARK_STOP
- : String.format(MARK_NAMED_STOP, mDisplayName);
- if (!mRunning) msg += " (packet listener stopped unexpectedly)";
- mLog.log(msg);
- }
-
- @Override
- protected void logError(String msg, Exception e) {
- Log.e(mTag, msg, e);
- addLogEntry(msg + e);
- }
-
- private void addLogEntry(String entry) {
- mLog.log(entry);
- }
- }
-}
diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java
deleted file mode 100644
index 266b1b0..0000000
--- a/packages/NetworkStack/src/android/net/ip/IpClient.java
+++ /dev/null
@@ -1,1784 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import static android.net.RouteInfo.RTN_UNICAST;
-import static android.net.shared.IpConfigurationParcelableUtil.toStableParcelable;
-
-import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.DhcpResults;
-import android.net.INetd;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.NattKeepalivePacketDataParcelable;
-import android.net.NetworkStackIpMemoryStore;
-import android.net.ProvisioningConfigurationParcelable;
-import android.net.ProxyInfo;
-import android.net.RouteInfo;
-import android.net.TcpKeepalivePacketDataParcelable;
-import android.net.apf.ApfCapabilities;
-import android.net.apf.ApfFilter;
-import android.net.dhcp.DhcpClient;
-import android.net.metrics.IpConnectivityLog;
-import android.net.metrics.IpManagerEvent;
-import android.net.shared.InitialConfiguration;
-import android.net.shared.ProvisioningConfiguration;
-import android.net.util.InterfaceParams;
-import android.net.util.SharedLog;
-import android.os.ConditionVariable;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.text.TextUtils;
-import android.util.LocalLog;
-import android.util.Log;
-import android.util.Pair;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IState;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.MessageUtils;
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-import com.android.internal.util.WakeupMessage;
-import com.android.server.NetworkObserverRegistry;
-import com.android.server.NetworkStackService.NetworkStackServiceManager;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.net.InetAddress;
-import java.util.Collection;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
-
-/**
- * IpClient
- *
- * This class provides the interface to IP-layer provisioning and maintenance
- * functionality that can be used by transport layers like Wi-Fi, Ethernet,
- * et cetera.
- *
- * [ Lifetime ]
- * IpClient is designed to be instantiated as soon as the interface name is
- * known and can be as long-lived as the class containing it (i.e. declaring
- * it "private final" is okay).
- *
- * @hide
- */
-public class IpClient extends StateMachine {
- private static final boolean DBG = false;
-
- // For message logging.
- private static final Class[] sMessageClasses = { IpClient.class, DhcpClient.class };
- private static final SparseArray<String> sWhatToString =
- MessageUtils.findMessageNames(sMessageClasses);
- // Two static concurrent hashmaps of interface name to logging classes.
- // One holds StateMachine logs and the other connectivity packet logs.
- private static final ConcurrentHashMap<String, SharedLog> sSmLogs = new ConcurrentHashMap<>();
- private static final ConcurrentHashMap<String, LocalLog> sPktLogs = new ConcurrentHashMap<>();
- private final NetworkStackIpMemoryStore mIpMemoryStore;
-
- /**
- * Dump all state machine and connectivity packet logs to the specified writer.
- * @param skippedIfaces Interfaces for which logs should not be dumped.
- */
- public static void dumpAllLogs(PrintWriter writer, Set<String> skippedIfaces) {
- for (String ifname : sSmLogs.keySet()) {
- if (skippedIfaces.contains(ifname)) continue;
-
- writer.println(String.format("--- BEGIN %s ---", ifname));
-
- final SharedLog smLog = sSmLogs.get(ifname);
- if (smLog != null) {
- writer.println("State machine log:");
- smLog.dump(null, writer, null);
- }
-
- writer.println("");
-
- final LocalLog pktLog = sPktLogs.get(ifname);
- if (pktLog != null) {
- writer.println("Connectivity packet log:");
- pktLog.readOnlyLocalLog().dump(null, writer, null);
- }
-
- writer.println(String.format("--- END %s ---", ifname));
- }
- }
-
- // Use a wrapper class to log in order to ensure complete and detailed
- // logging. This method is lighter weight than annotations/reflection
- // and has the following benefits:
- //
- // - No invoked method can be forgotten.
- // Any new method added to IpClient.Callback must be overridden
- // here or it will never be called.
- //
- // - No invoking call site can be forgotten.
- // Centralized logging in this way means call sites don't need to
- // remember to log, and therefore no call site can be forgotten.
- //
- // - No variation in log format among call sites.
- // Encourages logging of any available arguments, and all call sites
- // are necessarily logged identically.
- //
- // NOTE: Log first because passed objects may or may not be thread-safe and
- // once passed on to the callback they may be modified by another thread.
- //
- // TODO: Find an lighter weight approach.
- public static class IpClientCallbacksWrapper {
- private static final String PREFIX = "INVOKE ";
- private final IIpClientCallbacks mCallback;
- private final SharedLog mLog;
-
- @VisibleForTesting
- protected IpClientCallbacksWrapper(IIpClientCallbacks callback, SharedLog log) {
- mCallback = callback;
- mLog = log;
- }
-
- private void log(String msg) {
- mLog.log(PREFIX + msg);
- }
-
- private void log(String msg, Throwable e) {
- mLog.e(PREFIX + msg, e);
- }
-
- public void onPreDhcpAction() {
- log("onPreDhcpAction()");
- try {
- mCallback.onPreDhcpAction();
- } catch (RemoteException e) {
- log("Failed to call onPreDhcpAction", e);
- }
- }
-
- public void onPostDhcpAction() {
- log("onPostDhcpAction()");
- try {
- mCallback.onPostDhcpAction();
- } catch (RemoteException e) {
- log("Failed to call onPostDhcpAction", e);
- }
- }
-
- public void onNewDhcpResults(DhcpResults dhcpResults) {
- log("onNewDhcpResults({" + dhcpResults + "})");
- try {
- mCallback.onNewDhcpResults(toStableParcelable(dhcpResults));
- } catch (RemoteException e) {
- log("Failed to call onNewDhcpResults", e);
- }
- }
-
- public void onProvisioningSuccess(LinkProperties newLp) {
- log("onProvisioningSuccess({" + newLp + "})");
- try {
- mCallback.onProvisioningSuccess(newLp);
- } catch (RemoteException e) {
- log("Failed to call onProvisioningSuccess", e);
- }
- }
-
- public void onProvisioningFailure(LinkProperties newLp) {
- log("onProvisioningFailure({" + newLp + "})");
- try {
- mCallback.onProvisioningFailure(newLp);
- } catch (RemoteException e) {
- log("Failed to call onProvisioningFailure", e);
- }
- }
-
- public void onLinkPropertiesChange(LinkProperties newLp) {
- log("onLinkPropertiesChange({" + newLp + "})");
- try {
- mCallback.onLinkPropertiesChange(newLp);
- } catch (RemoteException e) {
- log("Failed to call onLinkPropertiesChange", e);
- }
- }
-
- public void onReachabilityLost(String logMsg) {
- log("onReachabilityLost(" + logMsg + ")");
- try {
- mCallback.onReachabilityLost(logMsg);
- } catch (RemoteException e) {
- log("Failed to call onReachabilityLost", e);
- }
- }
-
- public void onQuit() {
- log("onQuit()");
- try {
- mCallback.onQuit();
- } catch (RemoteException e) {
- log("Failed to call onQuit", e);
- }
- }
-
- public void installPacketFilter(byte[] filter) {
- log("installPacketFilter(byte[" + filter.length + "])");
- try {
- mCallback.installPacketFilter(filter);
- } catch (RemoteException e) {
- log("Failed to call installPacketFilter", e);
- }
- }
-
- public void startReadPacketFilter() {
- log("startReadPacketFilter()");
- try {
- mCallback.startReadPacketFilter();
- } catch (RemoteException e) {
- log("Failed to call startReadPacketFilter", e);
- }
- }
-
- public void setFallbackMulticastFilter(boolean enabled) {
- log("setFallbackMulticastFilter(" + enabled + ")");
- try {
- mCallback.setFallbackMulticastFilter(enabled);
- } catch (RemoteException e) {
- log("Failed to call setFallbackMulticastFilter", e);
- }
- }
-
- public void setNeighborDiscoveryOffload(boolean enable) {
- log("setNeighborDiscoveryOffload(" + enable + ")");
- try {
- mCallback.setNeighborDiscoveryOffload(enable);
- } catch (RemoteException e) {
- log("Failed to call setNeighborDiscoveryOffload", e);
- }
- }
- }
-
- public static final String DUMP_ARG_CONFIRM = "confirm";
-
- // Below constants are picked up by MessageUtils and exempt from ProGuard optimization.
- private static final int CMD_TERMINATE_AFTER_STOP = 1;
- private static final int CMD_STOP = 2;
- private static final int CMD_START = 3;
- private static final int CMD_CONFIRM = 4;
- private static final int EVENT_PRE_DHCP_ACTION_COMPLETE = 5;
- // Triggered by NetlinkTracker to communicate netlink events.
- private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 6;
- private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 7;
- private static final int CMD_UPDATE_HTTP_PROXY = 8;
- private static final int CMD_SET_MULTICAST_FILTER = 9;
- private static final int EVENT_PROVISIONING_TIMEOUT = 10;
- private static final int EVENT_DHCPACTION_TIMEOUT = 11;
- private static final int EVENT_READ_PACKET_FILTER_COMPLETE = 12;
- private static final int CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF = 13;
- private static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF = 14;
- private static final int CMD_UPDATE_L2KEY_GROUPHINT = 15;
-
- // Internal commands to use instead of trying to call transitionTo() inside
- // a given State's enter() method. Calling transitionTo() from enter/exit
- // encounters a Log.wtf() that can cause trouble on eng builds.
- private static final int CMD_JUMP_STARTED_TO_RUNNING = 100;
- private static final int CMD_JUMP_RUNNING_TO_STOPPING = 101;
- private static final int CMD_JUMP_STOPPING_TO_STOPPED = 102;
-
- // IpClient shares a handler with DhcpClient: commands must not overlap
- public static final int DHCPCLIENT_CMD_BASE = 1000;
-
- private static final int MAX_LOG_RECORDS = 500;
- private static final int MAX_PACKET_RECORDS = 100;
-
- private static final boolean NO_CALLBACKS = false;
- private static final boolean SEND_CALLBACKS = true;
-
- // This must match the interface prefix in clatd.c.
- // TODO: Revert this hack once IpClient and Nat464Xlat work in concert.
- private static final String CLAT_PREFIX = "v4-";
-
- private static final int IMMEDIATE_FAILURE_DURATION = 0;
-
- private static final int PROV_CHANGE_STILL_NOT_PROVISIONED = 1;
- private static final int PROV_CHANGE_LOST_PROVISIONING = 2;
- private static final int PROV_CHANGE_GAINED_PROVISIONING = 3;
- private static final int PROV_CHANGE_STILL_PROVISIONED = 4;
-
- private final State mStoppedState = new StoppedState();
- private final State mStoppingState = new StoppingState();
- private final State mStartedState = new StartedState();
- private final State mRunningState = new RunningState();
-
- private final String mTag;
- private final Context mContext;
- private final String mInterfaceName;
- private final String mClatInterfaceName;
- @VisibleForTesting
- protected final IpClientCallbacksWrapper mCallback;
- private final Dependencies mDependencies;
- private final CountDownLatch mShutdownLatch;
- private final ConnectivityManager mCm;
- private final INetd mNetd;
- private final NetworkObserverRegistry mObserverRegistry;
- private final IpClientLinkObserver mLinkObserver;
- private final WakeupMessage mProvisioningTimeoutAlarm;
- private final WakeupMessage mDhcpActionTimeoutAlarm;
- private final SharedLog mLog;
- private final LocalLog mConnectivityPacketLog;
- private final MessageHandlingLogger mMsgStateLogger;
- private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
- private final InterfaceController mInterfaceCtrl;
-
- private InterfaceParams mInterfaceParams;
-
- /**
- * Non-final member variables accessed only from within our StateMachine.
- */
- private LinkProperties mLinkProperties;
- private android.net.shared.ProvisioningConfiguration mConfiguration;
- private IpReachabilityMonitor mIpReachabilityMonitor;
- private DhcpClient mDhcpClient;
- private DhcpResults mDhcpResults;
- private String mTcpBufferSizes;
- private ProxyInfo mHttpProxy;
- private ApfFilter mApfFilter;
- private String mL2Key; // The L2 key for this network, for writing into the memory store
- private String mGroupHint; // The group hint for this network, for writing into the memory store
- private boolean mMulticastFiltering;
- private long mStartTimeMillis;
-
- /**
- * Reading the snapshot is an asynchronous operation initiated by invoking
- * Callback.startReadPacketFilter() and completed when the WiFi Service responds with an
- * EVENT_READ_PACKET_FILTER_COMPLETE message. The mApfDataSnapshotComplete condition variable
- * signals when a new snapshot is ready.
- */
- private final ConditionVariable mApfDataSnapshotComplete = new ConditionVariable();
-
- public static class Dependencies {
- /**
- * Get interface parameters for the specified interface.
- */
- public InterfaceParams getInterfaceParams(String ifname) {
- return InterfaceParams.getByName(ifname);
- }
-
- /**
- * Get a INetd connector.
- */
- public INetd getNetd(Context context) {
- return INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE));
- }
- }
-
- public IpClient(Context context, String ifName, IIpClientCallbacks callback,
- NetworkObserverRegistry observerRegistry, NetworkStackServiceManager nssManager) {
- this(context, ifName, callback, observerRegistry, nssManager, new Dependencies());
- }
-
- @VisibleForTesting
- IpClient(Context context, String ifName, IIpClientCallbacks callback,
- NetworkObserverRegistry observerRegistry, NetworkStackServiceManager nssManager,
- Dependencies deps) {
- super(IpClient.class.getSimpleName() + "." + ifName);
- Preconditions.checkNotNull(ifName);
- Preconditions.checkNotNull(callback);
-
- mTag = getName();
-
- mContext = context;
- mInterfaceName = ifName;
- mClatInterfaceName = CLAT_PREFIX + ifName;
- mDependencies = deps;
- mShutdownLatch = new CountDownLatch(1);
- mCm = mContext.getSystemService(ConnectivityManager.class);
- mObserverRegistry = observerRegistry;
- mIpMemoryStore =
- new NetworkStackIpMemoryStore(context, nssManager.getIpMemoryStoreService());
-
- sSmLogs.putIfAbsent(mInterfaceName, new SharedLog(MAX_LOG_RECORDS, mTag));
- mLog = sSmLogs.get(mInterfaceName);
- sPktLogs.putIfAbsent(mInterfaceName, new LocalLog(MAX_PACKET_RECORDS));
- mConnectivityPacketLog = sPktLogs.get(mInterfaceName);
- mMsgStateLogger = new MessageHandlingLogger();
- mCallback = new IpClientCallbacksWrapper(callback, mLog);
-
- // TODO: Consider creating, constructing, and passing in some kind of
- // InterfaceController.Dependencies class.
- mNetd = deps.getNetd(mContext);
- mInterfaceCtrl = new InterfaceController(mInterfaceName, mNetd, mLog);
-
- mLinkObserver = new IpClientLinkObserver(
- mInterfaceName,
- () -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED)) {
- @Override
- public void onInterfaceAdded(String iface) {
- super.onInterfaceAdded(iface);
- if (mClatInterfaceName.equals(iface)) {
- mCallback.setNeighborDiscoveryOffload(false);
- } else if (!mInterfaceName.equals(iface)) {
- return;
- }
-
- final String msg = "interfaceAdded(" + iface + ")";
- logMsg(msg);
- }
-
- @Override
- public void onInterfaceRemoved(String iface) {
- super.onInterfaceRemoved(iface);
- // TODO: Also observe mInterfaceName going down and take some
- // kind of appropriate action.
- if (mClatInterfaceName.equals(iface)) {
- // TODO: consider sending a message to the IpClient main
- // StateMachine thread, in case "NDO enabled" state becomes
- // tied to more things that 464xlat operation.
- mCallback.setNeighborDiscoveryOffload(true);
- } else if (!mInterfaceName.equals(iface)) {
- return;
- }
-
- final String msg = "interfaceRemoved(" + iface + ")";
- logMsg(msg);
- }
-
- private void logMsg(String msg) {
- Log.d(mTag, msg);
- getHandler().post(() -> mLog.log("OBSERVED " + msg));
- }
- };
-
- mLinkProperties = new LinkProperties();
- mLinkProperties.setInterfaceName(mInterfaceName);
-
- mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
- mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
- mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
- mTag + ".EVENT_DHCPACTION_TIMEOUT", EVENT_DHCPACTION_TIMEOUT);
-
- // Anything the StateMachine may access must have been instantiated
- // before this point.
- configureAndStartStateMachine();
-
- // Anything that may send messages to the StateMachine must only be
- // configured to do so after the StateMachine has started (above).
- startStateMachineUpdaters();
- }
-
- /**
- * Make a IIpClient connector to communicate with this IpClient.
- */
- public IIpClient makeConnector() {
- return new IpClientConnector();
- }
-
- class IpClientConnector extends IIpClient.Stub {
- @Override
- public void completedPreDhcpAction() {
- checkNetworkStackCallingPermission();
- IpClient.this.completedPreDhcpAction();
- }
- @Override
- public void confirmConfiguration() {
- checkNetworkStackCallingPermission();
- IpClient.this.confirmConfiguration();
- }
- @Override
- public void readPacketFilterComplete(byte[] data) {
- checkNetworkStackCallingPermission();
- IpClient.this.readPacketFilterComplete(data);
- }
- @Override
- public void shutdown() {
- checkNetworkStackCallingPermission();
- IpClient.this.shutdown();
- }
- @Override
- public void startProvisioning(ProvisioningConfigurationParcelable req) {
- checkNetworkStackCallingPermission();
- IpClient.this.startProvisioning(ProvisioningConfiguration.fromStableParcelable(req));
- }
- @Override
- public void stop() {
- checkNetworkStackCallingPermission();
- IpClient.this.stop();
- }
- @Override
- public void setL2KeyAndGroupHint(String l2Key, String groupHint) {
- checkNetworkStackCallingPermission();
- IpClient.this.setL2KeyAndGroupHint(l2Key, groupHint);
- }
- @Override
- public void setTcpBufferSizes(String tcpBufferSizes) {
- checkNetworkStackCallingPermission();
- IpClient.this.setTcpBufferSizes(tcpBufferSizes);
- }
- @Override
- public void setHttpProxy(ProxyInfo proxyInfo) {
- checkNetworkStackCallingPermission();
- IpClient.this.setHttpProxy(proxyInfo);
- }
- @Override
- public void setMulticastFilter(boolean enabled) {
- checkNetworkStackCallingPermission();
- IpClient.this.setMulticastFilter(enabled);
- }
- @Override
- public void addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt) {
- checkNetworkStackCallingPermission();
- IpClient.this.addKeepalivePacketFilter(slot, pkt);
- }
- @Override
- public void addNattKeepalivePacketFilter(int slot, NattKeepalivePacketDataParcelable pkt) {
- checkNetworkStackCallingPermission();
- IpClient.this.addNattKeepalivePacketFilter(slot, pkt);
- }
- @Override
- public void removeKeepalivePacketFilter(int slot) {
- checkNetworkStackCallingPermission();
- IpClient.this.removeKeepalivePacketFilter(slot);
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
- }
-
- public String getInterfaceName() {
- return mInterfaceName;
- }
-
- private void configureAndStartStateMachine() {
- // CHECKSTYLE:OFF IndentationCheck
- addState(mStoppedState);
- addState(mStartedState);
- addState(mRunningState, mStartedState);
- addState(mStoppingState);
- // CHECKSTYLE:ON IndentationCheck
-
- setInitialState(mStoppedState);
-
- super.start();
- }
-
- private void startStateMachineUpdaters() {
- mObserverRegistry.registerObserverForNonblockingCallback(mLinkObserver);
- }
-
- private void stopStateMachineUpdaters() {
- mObserverRegistry.unregisterObserver(mLinkObserver);
- }
-
- @Override
- protected void onQuitting() {
- mCallback.onQuit();
- mShutdownLatch.countDown();
- }
-
- /**
- * Shut down this IpClient instance altogether.
- */
- public void shutdown() {
- stop();
- sendMessage(CMD_TERMINATE_AFTER_STOP);
- }
-
- /**
- * Start provisioning with the provided parameters.
- */
- public void startProvisioning(ProvisioningConfiguration req) {
- if (!req.isValid()) {
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
- return;
- }
-
- mInterfaceParams = mDependencies.getInterfaceParams(mInterfaceName);
- if (mInterfaceParams == null) {
- logError("Failed to find InterfaceParams for " + mInterfaceName);
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_INTERFACE_NOT_FOUND);
- return;
- }
-
- mCallback.setNeighborDiscoveryOffload(true);
- sendMessage(CMD_START, new android.net.shared.ProvisioningConfiguration(req));
- }
-
- /**
- * Stop this IpClient.
- *
- * <p>This does not shut down the StateMachine itself, which is handled by {@link #shutdown()}.
- */
- public void stop() {
- sendMessage(CMD_STOP);
- }
-
- /**
- * Confirm the provisioning configuration.
- */
- public void confirmConfiguration() {
- sendMessage(CMD_CONFIRM);
- }
-
- /**
- * For clients using {@link ProvisioningConfiguration.Builder#withPreDhcpAction()}, must be
- * called after {@link IIpClientCallbacks#onPreDhcpAction} to indicate that DHCP is clear to
- * proceed.
- */
- public void completedPreDhcpAction() {
- sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
- }
-
- /**
- * Indicate that packet filter read is complete.
- */
- public void readPacketFilterComplete(byte[] data) {
- sendMessage(EVENT_READ_PACKET_FILTER_COMPLETE, data);
- }
-
- /**
- * Set the TCP buffer sizes to use.
- *
- * This may be called, repeatedly, at any time before or after a call to
- * #startProvisioning(). The setting is cleared upon calling #stop().
- */
- public void setTcpBufferSizes(String tcpBufferSizes) {
- sendMessage(CMD_UPDATE_TCP_BUFFER_SIZES, tcpBufferSizes);
- }
-
- /**
- * Set the L2 key and group hint for storing info into the memory store.
- */
- public void setL2KeyAndGroupHint(String l2Key, String groupHint) {
- sendMessage(CMD_UPDATE_L2KEY_GROUPHINT, new Pair<>(l2Key, groupHint));
- }
-
- /**
- * Set the HTTP Proxy configuration to use.
- *
- * This may be called, repeatedly, at any time before or after a call to
- * #startProvisioning(). The setting is cleared upon calling #stop().
- */
- public void setHttpProxy(ProxyInfo proxyInfo) {
- sendMessage(CMD_UPDATE_HTTP_PROXY, proxyInfo);
- }
-
- /**
- * Enable or disable the multicast filter. Attempts to use APF to accomplish the filtering,
- * if not, Callback.setFallbackMulticastFilter() is called.
- */
- public void setMulticastFilter(boolean enabled) {
- sendMessage(CMD_SET_MULTICAST_FILTER, enabled);
- }
-
- /**
- * Called by WifiStateMachine to add TCP keepalive packet filter before setting up
- * keepalive offload.
- */
- public void addKeepalivePacketFilter(int slot, @NonNull TcpKeepalivePacketDataParcelable pkt) {
- sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */, pkt);
- }
-
- /**
- * Called by WifiStateMachine to add NATT keepalive packet filter before setting up
- * keepalive offload.
- */
- public void addNattKeepalivePacketFilter(int slot,
- @NonNull NattKeepalivePacketDataParcelable pkt) {
- sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */ , pkt);
- }
-
- /**
- * Called by WifiStateMachine to remove keepalive packet filter after stopping keepalive
- * offload.
- */
- public void removeKeepalivePacketFilter(int slot) {
- sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF, slot, 0 /* Unused */);
- }
-
- /**
- * Dump logs of this IpClient.
- */
- public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- if (args != null && args.length > 0 && DUMP_ARG_CONFIRM.equals(args[0])) {
- // Execute confirmConfiguration() and take no further action.
- confirmConfiguration();
- return;
- }
-
- // Thread-unsafe access to mApfFilter but just used for debugging.
- final ApfFilter apfFilter = mApfFilter;
- final android.net.shared.ProvisioningConfiguration provisioningConfig = mConfiguration;
- final ApfCapabilities apfCapabilities = (provisioningConfig != null)
- ? provisioningConfig.mApfCapabilities : null;
-
- IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
- pw.println(mTag + " APF dump:");
- pw.increaseIndent();
- if (apfFilter != null) {
- if (apfCapabilities.hasDataAccess()) {
- // Request a new snapshot, then wait for it.
- mApfDataSnapshotComplete.close();
- mCallback.startReadPacketFilter();
- if (!mApfDataSnapshotComplete.block(1000)) {
- pw.print("TIMEOUT: DUMPING STALE APF SNAPSHOT");
- }
- }
- apfFilter.dump(pw);
-
- } else {
- pw.print("No active ApfFilter; ");
- if (provisioningConfig == null) {
- pw.println("IpClient not yet started.");
- } else if (apfCapabilities == null || apfCapabilities.apfVersionSupported == 0) {
- pw.println("Hardware does not support APF.");
- } else {
- pw.println("ApfFilter not yet started, APF capabilities: " + apfCapabilities);
- }
- }
- pw.decreaseIndent();
- pw.println();
- pw.println(mTag + " current ProvisioningConfiguration:");
- pw.increaseIndent();
- pw.println(Objects.toString(provisioningConfig, "N/A"));
- pw.decreaseIndent();
-
- final IpReachabilityMonitor iprm = mIpReachabilityMonitor;
- if (iprm != null) {
- pw.println();
- pw.println(mTag + " current IpReachabilityMonitor state:");
- pw.increaseIndent();
- iprm.dump(pw);
- pw.decreaseIndent();
- }
-
- pw.println();
- pw.println(mTag + " StateMachine dump:");
- pw.increaseIndent();
- mLog.dump(fd, pw, args);
- pw.decreaseIndent();
-
- pw.println();
- pw.println(mTag + " connectivity packet log:");
- pw.println();
- pw.println("Debug with python and scapy via:");
- pw.println("shell$ python");
- pw.println(">>> from scapy import all as scapy");
- pw.println(">>> scapy.Ether(\"<paste_hex_string>\".decode(\"hex\")).show2()");
- pw.println();
-
- pw.increaseIndent();
- mConnectivityPacketLog.readOnlyLocalLog().dump(fd, pw, args);
- pw.decreaseIndent();
- }
-
-
- /**
- * Internals.
- */
-
- @Override
- protected String getWhatToString(int what) {
- return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what));
- }
-
- @Override
- protected String getLogRecString(Message msg) {
- final String logLine = String.format(
- "%s/%d %d %d %s [%s]",
- mInterfaceName, (mInterfaceParams == null) ? -1 : mInterfaceParams.index,
- msg.arg1, msg.arg2, Objects.toString(msg.obj), mMsgStateLogger);
-
- final String richerLogLine = getWhatToString(msg.what) + " " + logLine;
- mLog.log(richerLogLine);
- if (DBG) {
- Log.d(mTag, richerLogLine);
- }
-
- mMsgStateLogger.reset();
- return logLine;
- }
-
- @Override
- protected boolean recordLogRec(Message msg) {
- // Don't log EVENT_NETLINK_LINKPROPERTIES_CHANGED. They can be noisy,
- // and we already log any LinkProperties change that results in an
- // invocation of IpClient.Callback#onLinkPropertiesChange().
- final boolean shouldLog = (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED);
- if (!shouldLog) {
- mMsgStateLogger.reset();
- }
- return shouldLog;
- }
-
- private void logError(String fmt, Object... args) {
- final String msg = "ERROR " + String.format(fmt, args);
- Log.e(mTag, msg);
- mLog.log(msg);
- }
-
- // This needs to be called with care to ensure that our LinkProperties
- // are in sync with the actual LinkProperties of the interface. For example,
- // we should only call this if we know for sure that there are no IP addresses
- // assigned to the interface, etc.
- private void resetLinkProperties() {
- mLinkObserver.clearLinkProperties();
- mConfiguration = null;
- mDhcpResults = null;
- mTcpBufferSizes = "";
- mHttpProxy = null;
-
- mLinkProperties = new LinkProperties();
- mLinkProperties.setInterfaceName(mInterfaceName);
- }
-
- private void recordMetric(final int type) {
- // We may record error metrics prior to starting.
- // Map this to IMMEDIATE_FAILURE_DURATION.
- final long duration = (mStartTimeMillis > 0)
- ? (SystemClock.elapsedRealtime() - mStartTimeMillis)
- : IMMEDIATE_FAILURE_DURATION;
- mMetricsLog.log(mInterfaceName, new IpManagerEvent(type, duration));
- }
-
- // For now: use WifiStateMachine's historical notion of provisioned.
- @VisibleForTesting
- static boolean isProvisioned(LinkProperties lp, InitialConfiguration config) {
- // For historical reasons, we should connect even if all we have is
- // an IPv4 address and nothing else.
- if (lp.hasIpv4Address() || lp.isProvisioned()) {
- return true;
- }
- if (config == null) {
- return false;
- }
-
- // When an InitialConfiguration is specified, ignore any difference with previous
- // properties and instead check if properties observed match the desired properties.
- return config.isProvisionedBy(lp.getLinkAddresses(), lp.getRoutes());
- }
-
- // TODO: Investigate folding all this into the existing static function
- // LinkProperties.compareProvisioning() or some other single function that
- // takes two LinkProperties objects and returns a ProvisioningChange
- // object that is a correct and complete assessment of what changed, taking
- // account of the asymmetries described in the comments in this function.
- // Then switch to using it everywhere (IpReachabilityMonitor, etc.).
- private int compareProvisioning(LinkProperties oldLp, LinkProperties newLp) {
- int delta;
- InitialConfiguration config = mConfiguration != null ? mConfiguration.mInitialConfig : null;
- final boolean wasProvisioned = isProvisioned(oldLp, config);
- final boolean isProvisioned = isProvisioned(newLp, config);
-
- if (!wasProvisioned && isProvisioned) {
- delta = PROV_CHANGE_GAINED_PROVISIONING;
- } else if (wasProvisioned && isProvisioned) {
- delta = PROV_CHANGE_STILL_PROVISIONED;
- } else if (!wasProvisioned && !isProvisioned) {
- delta = PROV_CHANGE_STILL_NOT_PROVISIONED;
- } else {
- // (wasProvisioned && !isProvisioned)
- //
- // Note that this is true even if we lose a configuration element
- // (e.g., a default gateway) that would not be required to advance
- // into provisioned state. This is intended: if we have a default
- // router and we lose it, that's a sure sign of a problem, but if
- // we connect to a network with no IPv4 DNS servers, we consider
- // that to be a network without DNS servers and connect anyway.
- //
- // See the comment below.
- delta = PROV_CHANGE_LOST_PROVISIONING;
- }
-
- final boolean lostIPv6 = oldLp.isIpv6Provisioned() && !newLp.isIpv6Provisioned();
- final boolean lostIPv4Address = oldLp.hasIpv4Address() && !newLp.hasIpv4Address();
- final boolean lostIPv6Router = oldLp.hasIpv6DefaultRoute() && !newLp.hasIpv6DefaultRoute();
-
- // If bad wifi avoidance is disabled, then ignore IPv6 loss of
- // provisioning. Otherwise, when a hotspot that loses Internet
- // access sends out a 0-lifetime RA to its clients, the clients
- // will disconnect and then reconnect, avoiding the bad hotspot,
- // instead of getting stuck on the bad hotspot. http://b/31827713 .
- //
- // This is incorrect because if the hotspot then regains Internet
- // access with a different prefix, TCP connections on the
- // deprecated addresses will remain stuck.
- //
- // Note that we can still be disconnected by IpReachabilityMonitor
- // if the IPv6 default gateway (but not the IPv6 DNS servers; see
- // accompanying code in IpReachabilityMonitor) is unreachable.
- final boolean ignoreIPv6ProvisioningLoss =
- mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker
- && mCm.shouldAvoidBadWifi();
-
- // Additionally:
- //
- // Partial configurations (e.g., only an IPv4 address with no DNS
- // servers and no default route) are accepted as long as DHCPv4
- // succeeds. On such a network, isProvisioned() will always return
- // false, because the configuration is not complete, but we want to
- // connect anyway. It might be a disconnected network such as a
- // Chromecast or a wireless printer, for example.
- //
- // Because on such a network isProvisioned() will always return false,
- // delta will never be LOST_PROVISIONING. So check for loss of
- // provisioning here too.
- if (lostIPv4Address || (lostIPv6 && !ignoreIPv6ProvisioningLoss)) {
- delta = PROV_CHANGE_LOST_PROVISIONING;
- }
-
- // Additionally:
- //
- // If the previous link properties had a global IPv6 address and an
- // IPv6 default route then also consider the loss of that default route
- // to be a loss of provisioning. See b/27962810.
- if (oldLp.hasGlobalIpv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) {
- delta = PROV_CHANGE_LOST_PROVISIONING;
- }
-
- return delta;
- }
-
- private void dispatchCallback(int delta, LinkProperties newLp) {
- switch (delta) {
- case PROV_CHANGE_GAINED_PROVISIONING:
- if (DBG) {
- Log.d(mTag, "onProvisioningSuccess()");
- }
- recordMetric(IpManagerEvent.PROVISIONING_OK);
- mCallback.onProvisioningSuccess(newLp);
- break;
-
- case PROV_CHANGE_LOST_PROVISIONING:
- if (DBG) {
- Log.d(mTag, "onProvisioningFailure()");
- }
- recordMetric(IpManagerEvent.PROVISIONING_FAIL);
- mCallback.onProvisioningFailure(newLp);
- break;
-
- default:
- if (DBG) {
- Log.d(mTag, "onLinkPropertiesChange()");
- }
- mCallback.onLinkPropertiesChange(newLp);
- break;
- }
- }
-
- // Updates all IpClient-related state concerned with LinkProperties.
- // Returns a ProvisioningChange for possibly notifying other interested
- // parties that are not fronted by IpClient.
- private int setLinkProperties(LinkProperties newLp) {
- if (mApfFilter != null) {
- mApfFilter.setLinkProperties(newLp);
- }
- if (mIpReachabilityMonitor != null) {
- mIpReachabilityMonitor.updateLinkProperties(newLp);
- }
-
- int delta = compareProvisioning(mLinkProperties, newLp);
- mLinkProperties = new LinkProperties(newLp);
-
- if (delta == PROV_CHANGE_GAINED_PROVISIONING) {
- // TODO: Add a proper ProvisionedState and cancel the alarm in
- // its enter() method.
- mProvisioningTimeoutAlarm.cancel();
- }
-
- return delta;
- }
-
- private LinkProperties assembleLinkProperties() {
- // [1] Create a new LinkProperties object to populate.
- LinkProperties newLp = new LinkProperties();
- newLp.setInterfaceName(mInterfaceName);
-
- // [2] Pull in data from netlink:
- // - IPv4 addresses
- // - IPv6 addresses
- // - IPv6 routes
- // - IPv6 DNS servers
- //
- // N.B.: this is fundamentally race-prone and should be fixed by
- // changing IpClientLinkObserver from a hybrid edge/level model to an
- // edge-only model, or by giving IpClient its own netlink socket(s)
- // so as to track all required information directly.
- LinkProperties netlinkLinkProperties = mLinkObserver.getLinkProperties();
- newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
- for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
- newLp.addRoute(route);
- }
- addAllReachableDnsServers(newLp, netlinkLinkProperties.getDnsServers());
-
- // [3] Add in data from DHCPv4, if available.
- //
- // mDhcpResults is never shared with any other owner so we don't have
- // to worry about concurrent modification.
- if (mDhcpResults != null) {
- final List<RouteInfo> routes =
- mDhcpResults.toStaticIpConfiguration().getRoutes(mInterfaceName);
- for (RouteInfo route : routes) {
- newLp.addRoute(route);
- }
- addAllReachableDnsServers(newLp, mDhcpResults.dnsServers);
- newLp.setDomains(mDhcpResults.domains);
-
- if (mDhcpResults.mtu != 0) {
- newLp.setMtu(mDhcpResults.mtu);
- }
- }
-
- // [4] Add in TCP buffer sizes and HTTP Proxy config, if available.
- if (!TextUtils.isEmpty(mTcpBufferSizes)) {
- newLp.setTcpBufferSizes(mTcpBufferSizes);
- }
- if (mHttpProxy != null) {
- newLp.setHttpProxy(mHttpProxy);
- }
-
- // [5] Add data from InitialConfiguration
- if (mConfiguration != null && mConfiguration.mInitialConfig != null) {
- InitialConfiguration config = mConfiguration.mInitialConfig;
- // Add InitialConfiguration routes and dns server addresses once all addresses
- // specified in the InitialConfiguration have been observed with Netlink.
- if (config.isProvisionedBy(newLp.getLinkAddresses(), null)) {
- for (IpPrefix prefix : config.directlyConnectedRoutes) {
- newLp.addRoute(new RouteInfo(prefix, null, mInterfaceName, RTN_UNICAST));
- }
- }
- addAllReachableDnsServers(newLp, config.dnsServers);
- }
- final LinkProperties oldLp = mLinkProperties;
- if (DBG) {
- Log.d(mTag, String.format("Netlink-seen LPs: %s, new LPs: %s; old LPs: %s",
- netlinkLinkProperties, newLp, oldLp));
- }
-
- // TODO: also learn via netlink routes specified by an InitialConfiguration and specified
- // from a static IP v4 config instead of manually patching them in in steps [3] and [5].
- return newLp;
- }
-
- private static void addAllReachableDnsServers(
- LinkProperties lp, Iterable<InetAddress> dnses) {
- // TODO: Investigate deleting this reachability check. We should be
- // able to pass everything down to netd and let netd do evaluation
- // and RFC6724-style sorting.
- for (InetAddress dns : dnses) {
- if (!dns.isAnyLocalAddress() && lp.isReachable(dns)) {
- lp.addDnsServer(dns);
- }
- }
- }
-
- // Returns false if we have lost provisioning, true otherwise.
- private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) {
- final LinkProperties newLp = assembleLinkProperties();
- if (Objects.equals(newLp, mLinkProperties)) {
- return true;
- }
- final int delta = setLinkProperties(newLp);
- // Most of the attributes stored in the memory store are deduced from
- // the link properties, therefore when the properties update the memory
- // store record should be updated too.
- maybeSaveNetworkToIpMemoryStore();
- if (sendCallbacks) {
- dispatchCallback(delta, newLp);
- }
- return (delta != PROV_CHANGE_LOST_PROVISIONING);
- }
-
- private void handleIPv4Success(DhcpResults dhcpResults) {
- mDhcpResults = new DhcpResults(dhcpResults);
- final LinkProperties newLp = assembleLinkProperties();
- final int delta = setLinkProperties(newLp);
-
- if (DBG) {
- Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
- }
- mCallback.onNewDhcpResults(dhcpResults);
- maybeSaveNetworkToIpMemoryStore();
- dispatchCallback(delta, newLp);
- }
-
- private void handleIPv4Failure() {
- // TODO: Investigate deleting this clearIPv4Address() call.
- //
- // DhcpClient will send us CMD_CLEAR_LINKADDRESS in all circumstances
- // that could trigger a call to this function. If we missed handling
- // that message in StartedState for some reason we would still clear
- // any addresses upon entry to StoppedState.
- mInterfaceCtrl.clearIPv4Address();
- mDhcpResults = null;
- if (DBG) {
- Log.d(mTag, "onNewDhcpResults(null)");
- }
- mCallback.onNewDhcpResults(null);
-
- handleProvisioningFailure();
- }
-
- private void handleProvisioningFailure() {
- final LinkProperties newLp = assembleLinkProperties();
- int delta = setLinkProperties(newLp);
- // If we've gotten here and we're still not provisioned treat that as
- // a total loss of provisioning.
- //
- // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
- // there was no usable IPv6 obtained before a non-zero provisioning
- // timeout expired.
- //
- // Regardless: GAME OVER.
- if (delta == PROV_CHANGE_STILL_NOT_PROVISIONED) {
- delta = PROV_CHANGE_LOST_PROVISIONING;
- }
-
- dispatchCallback(delta, newLp);
- if (delta == PROV_CHANGE_LOST_PROVISIONING) {
- transitionTo(mStoppingState);
- }
- }
-
- private void doImmediateProvisioningFailure(int failureType) {
- logError("onProvisioningFailure(): %s", failureType);
- recordMetric(failureType);
- mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties));
- }
-
- private boolean startIPv4() {
- // If we have a StaticIpConfiguration attempt to apply it and
- // handle the result accordingly.
- if (mConfiguration.mStaticIpConfig != null) {
- if (mInterfaceCtrl.setIPv4Address(mConfiguration.mStaticIpConfig.getIpAddress())) {
- handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
- } else {
- return false;
- }
- } else {
- // Start DHCPv4.
- mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams);
- mDhcpClient.registerForPreDhcpNotification();
- mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);
- }
-
- return true;
- }
-
- private boolean startIPv6() {
- return mInterfaceCtrl.setIPv6PrivacyExtensions(true)
- && mInterfaceCtrl.setIPv6AddrGenModeIfSupported(mConfiguration.mIPv6AddrGenMode)
- && mInterfaceCtrl.enableIPv6();
- }
-
- private boolean applyInitialConfig(InitialConfiguration config) {
- // TODO: also support specifying a static IPv4 configuration in InitialConfiguration.
- for (LinkAddress addr : findAll(config.ipAddresses, LinkAddress::isIpv6)) {
- if (!mInterfaceCtrl.addAddress(addr)) return false;
- }
-
- return true;
- }
-
- private boolean startIpReachabilityMonitor() {
- try {
- // TODO: Fetch these parameters from settings, and install a
- // settings observer to watch for update and re-program these
- // parameters (Q: is this level of dynamic updatability really
- // necessary or does reading from settings at startup suffice?).
- final int numSolicits = 5;
- final int interSolicitIntervalMs = 750;
- setNeighborParameters(mNetd, mInterfaceName, numSolicits, interSolicitIntervalMs);
- } catch (Exception e) {
- mLog.e("Failed to adjust neighbor parameters", e);
- // Carry on using the system defaults (currently: 3, 1000);
- }
-
- try {
- mIpReachabilityMonitor = new IpReachabilityMonitor(
- mContext,
- mInterfaceParams,
- getHandler(),
- mLog,
- new IpReachabilityMonitor.Callback() {
- @Override
- public void notifyLost(InetAddress ip, String logMsg) {
- mCallback.onReachabilityLost(logMsg);
- }
- },
- mConfiguration.mUsingMultinetworkPolicyTracker);
- } catch (IllegalArgumentException iae) {
- // Failed to start IpReachabilityMonitor. Log it and call
- // onProvisioningFailure() immediately.
- //
- // See http://b/31038971.
- logError("IpReachabilityMonitor failure: %s", iae);
- mIpReachabilityMonitor = null;
- }
-
- return (mIpReachabilityMonitor != null);
- }
-
- private void stopAllIP() {
- // We don't need to worry about routes, just addresses, because:
- // - disableIpv6() will clear autoconf IPv6 routes as well, and
- // - we don't get IPv4 routes from netlink
- // so we neither react to nor need to wait for changes in either.
-
- mInterfaceCtrl.disableIPv6();
- mInterfaceCtrl.clearAllAddresses();
- }
-
- private void maybeSaveNetworkToIpMemoryStore() {
- // TODO : implement this
- }
-
- class StoppedState extends State {
- @Override
- public void enter() {
- stopAllIP();
-
- resetLinkProperties();
- if (mStartTimeMillis > 0) {
- // Completed a life-cycle; send a final empty LinkProperties
- // (cleared in resetLinkProperties() above) and record an event.
- mCallback.onLinkPropertiesChange(new LinkProperties(mLinkProperties));
- recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE);
- mStartTimeMillis = 0;
- }
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_TERMINATE_AFTER_STOP:
- stopStateMachineUpdaters();
- quit();
- break;
-
- case CMD_STOP:
- break;
-
- case CMD_START:
- mConfiguration = (android.net.shared.ProvisioningConfiguration) msg.obj;
- transitionTo(mStartedState);
- break;
-
- case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
- handleLinkPropertiesUpdate(NO_CALLBACKS);
- break;
-
- case CMD_UPDATE_TCP_BUFFER_SIZES:
- mTcpBufferSizes = (String) msg.obj;
- handleLinkPropertiesUpdate(NO_CALLBACKS);
- break;
-
- case CMD_UPDATE_HTTP_PROXY:
- mHttpProxy = (ProxyInfo) msg.obj;
- handleLinkPropertiesUpdate(NO_CALLBACKS);
- break;
-
- case CMD_UPDATE_L2KEY_GROUPHINT: {
- final Pair<String, String> args = (Pair<String, String>) msg.obj;
- mL2Key = args.first;
- mGroupHint = args.second;
- break;
- }
-
- case CMD_SET_MULTICAST_FILTER:
- mMulticastFiltering = (boolean) msg.obj;
- break;
-
- case DhcpClient.CMD_ON_QUIT:
- // Everything is already stopped.
- logError("Unexpected CMD_ON_QUIT (already stopped).");
- break;
-
- default:
- return NOT_HANDLED;
- }
-
- mMsgStateLogger.handled(this, getCurrentState());
- return HANDLED;
- }
- }
-
- class StoppingState extends State {
- @Override
- public void enter() {
- if (mDhcpClient == null) {
- // There's no DHCPv4 for which to wait; proceed to stopped.
- deferMessage(obtainMessage(CMD_JUMP_STOPPING_TO_STOPPED));
- }
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_JUMP_STOPPING_TO_STOPPED:
- transitionTo(mStoppedState);
- break;
-
- case CMD_STOP:
- break;
-
- case DhcpClient.CMD_CLEAR_LINKADDRESS:
- mInterfaceCtrl.clearIPv4Address();
- break;
-
- case DhcpClient.CMD_ON_QUIT:
- mDhcpClient = null;
- transitionTo(mStoppedState);
- break;
-
- default:
- deferMessage(msg);
- }
-
- mMsgStateLogger.handled(this, getCurrentState());
- return HANDLED;
- }
- }
-
- class StartedState extends State {
- @Override
- public void enter() {
- mStartTimeMillis = SystemClock.elapsedRealtime();
-
- if (mConfiguration.mProvisioningTimeoutMs > 0) {
- final long alarmTime = SystemClock.elapsedRealtime()
- + mConfiguration.mProvisioningTimeoutMs;
- mProvisioningTimeoutAlarm.schedule(alarmTime);
- }
-
- if (readyToProceed()) {
- deferMessage(obtainMessage(CMD_JUMP_STARTED_TO_RUNNING));
- } else {
- // Clear all IPv4 and IPv6 before proceeding to RunningState.
- // Clean up any leftover state from an abnormal exit from
- // tethering or during an IpClient restart.
- stopAllIP();
- }
- }
-
- @Override
- public void exit() {
- mProvisioningTimeoutAlarm.cancel();
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_JUMP_STARTED_TO_RUNNING:
- transitionTo(mRunningState);
- break;
-
- case CMD_STOP:
- transitionTo(mStoppingState);
- break;
-
- case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
- handleLinkPropertiesUpdate(NO_CALLBACKS);
- if (readyToProceed()) {
- transitionTo(mRunningState);
- }
- break;
-
- case CMD_UPDATE_L2KEY_GROUPHINT: {
- final Pair<String, String> args = (Pair<String, String>) msg.obj;
- mL2Key = args.first;
- mGroupHint = args.second;
- // TODO : attributes should be saved to the memory store with
- // these new values if they differ from the previous ones.
- // If the state machine is in pure StartedState, then the values to input
- // are not known yet and should be updated when the LinkProperties are updated.
- // If the state machine is in RunningState (which is a child of StartedState)
- // then the next NUD check should be used to store the new values to avoid
- // inputting current values for what may be a different L3 network.
- break;
- }
-
- case EVENT_PROVISIONING_TIMEOUT:
- handleProvisioningFailure();
- break;
-
- default:
- // It's safe to process messages out of order because the
- // only message that can both
- // a) be received at this time and
- // b) affect provisioning state
- // is EVENT_NETLINK_LINKPROPERTIES_CHANGED (handled above).
- deferMessage(msg);
- }
-
- mMsgStateLogger.handled(this, getCurrentState());
- return HANDLED;
- }
-
- private boolean readyToProceed() {
- return (!mLinkProperties.hasIpv4Address() && !mLinkProperties.hasGlobalIpv6Address());
- }
- }
-
- class RunningState extends State {
- private ConnectivityPacketTracker mPacketTracker;
- private boolean mDhcpActionInFlight;
-
- @Override
- public void enter() {
- ApfFilter.ApfConfiguration apfConfig = new ApfFilter.ApfConfiguration();
- apfConfig.apfCapabilities = mConfiguration.mApfCapabilities;
- apfConfig.multicastFilter = mMulticastFiltering;
- // Get the Configuration for ApfFilter from Context
- apfConfig.ieee802_3Filter = ApfCapabilities.getApfDrop8023Frames();
- apfConfig.ethTypeBlackList = ApfCapabilities.getApfEtherTypeBlackList();
- mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback);
- // TODO: investigate the effects of any multicast filtering racing/interfering with the
- // rest of this IP configuration startup.
- if (mApfFilter == null) {
- mCallback.setFallbackMulticastFilter(mMulticastFiltering);
- }
-
- mPacketTracker = createPacketTracker();
- if (mPacketTracker != null) mPacketTracker.start(mConfiguration.mDisplayName);
-
- if (mConfiguration.mEnableIPv6 && !startIPv6()) {
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
- enqueueJumpToStoppingState();
- return;
- }
-
- if (mConfiguration.mEnableIPv4 && !startIPv4()) {
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4);
- enqueueJumpToStoppingState();
- return;
- }
-
- final InitialConfiguration config = mConfiguration.mInitialConfig;
- if ((config != null) && !applyInitialConfig(config)) {
- // TODO introduce a new IpManagerEvent constant to distinguish this error case.
- doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
- enqueueJumpToStoppingState();
- return;
- }
-
- if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
- doImmediateProvisioningFailure(
- IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
- enqueueJumpToStoppingState();
- return;
- }
- }
-
- @Override
- public void exit() {
- stopDhcpAction();
-
- if (mIpReachabilityMonitor != null) {
- mIpReachabilityMonitor.stop();
- mIpReachabilityMonitor = null;
- }
-
- if (mDhcpClient != null) {
- mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
- mDhcpClient.doQuit();
- }
-
- if (mPacketTracker != null) {
- mPacketTracker.stop();
- mPacketTracker = null;
- }
-
- if (mApfFilter != null) {
- mApfFilter.shutdown();
- mApfFilter = null;
- }
-
- resetLinkProperties();
- }
-
- private void enqueueJumpToStoppingState() {
- deferMessage(obtainMessage(CMD_JUMP_RUNNING_TO_STOPPING));
- }
-
- private ConnectivityPacketTracker createPacketTracker() {
- try {
- return new ConnectivityPacketTracker(
- getHandler(), mInterfaceParams, mConnectivityPacketLog);
- } catch (IllegalArgumentException e) {
- return null;
- }
- }
-
- private void ensureDhcpAction() {
- if (!mDhcpActionInFlight) {
- mCallback.onPreDhcpAction();
- mDhcpActionInFlight = true;
- final long alarmTime = SystemClock.elapsedRealtime()
- + mConfiguration.mRequestedPreDhcpActionMs;
- mDhcpActionTimeoutAlarm.schedule(alarmTime);
- }
- }
-
- private void stopDhcpAction() {
- mDhcpActionTimeoutAlarm.cancel();
- if (mDhcpActionInFlight) {
- mCallback.onPostDhcpAction();
- mDhcpActionInFlight = false;
- }
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_JUMP_RUNNING_TO_STOPPING:
- case CMD_STOP:
- transitionTo(mStoppingState);
- break;
-
- case CMD_START:
- logError("ALERT: START received in StartedState. Please fix caller.");
- break;
-
- case CMD_CONFIRM:
- // TODO: Possibly introduce a second type of confirmation
- // that both probes (a) on-link neighbors and (b) does
- // a DHCPv4 RENEW. We used to do this on Wi-Fi framework
- // roams.
- if (mIpReachabilityMonitor != null) {
- mIpReachabilityMonitor.probeAll();
- }
- break;
-
- case EVENT_PRE_DHCP_ACTION_COMPLETE:
- // It's possible to reach here if, for example, someone
- // calls completedPreDhcpAction() after provisioning with
- // a static IP configuration.
- if (mDhcpClient != null) {
- mDhcpClient.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE);
- }
- break;
-
- case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
- if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) {
- transitionTo(mStoppingState);
- }
- break;
-
- case CMD_UPDATE_TCP_BUFFER_SIZES:
- mTcpBufferSizes = (String) msg.obj;
- // This cannot possibly change provisioning state.
- handleLinkPropertiesUpdate(SEND_CALLBACKS);
- break;
-
- case CMD_UPDATE_HTTP_PROXY:
- mHttpProxy = (ProxyInfo) msg.obj;
- // This cannot possibly change provisioning state.
- handleLinkPropertiesUpdate(SEND_CALLBACKS);
- break;
-
- case CMD_SET_MULTICAST_FILTER: {
- mMulticastFiltering = (boolean) msg.obj;
- if (mApfFilter != null) {
- mApfFilter.setMulticastFilter(mMulticastFiltering);
- } else {
- mCallback.setFallbackMulticastFilter(mMulticastFiltering);
- }
- break;
- }
-
- case EVENT_READ_PACKET_FILTER_COMPLETE: {
- if (mApfFilter != null) {
- mApfFilter.setDataSnapshot((byte[]) msg.obj);
- }
- mApfDataSnapshotComplete.open();
- break;
- }
-
- case CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF: {
- final int slot = msg.arg1;
-
- if (mApfFilter != null) {
- if (msg.obj instanceof NattKeepalivePacketDataParcelable) {
- mApfFilter.addNattKeepalivePacketFilter(slot,
- (NattKeepalivePacketDataParcelable) msg.obj);
- } else if (msg.obj instanceof TcpKeepalivePacketDataParcelable) {
- mApfFilter.addTcpKeepalivePacketFilter(slot,
- (TcpKeepalivePacketDataParcelable) msg.obj);
- }
- }
- break;
- }
-
- case CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF: {
- final int slot = msg.arg1;
- if (mApfFilter != null) {
- mApfFilter.removeKeepalivePacketFilter(slot);
- }
- break;
- }
-
- case EVENT_DHCPACTION_TIMEOUT:
- stopDhcpAction();
- break;
-
- case DhcpClient.CMD_PRE_DHCP_ACTION:
- if (mConfiguration.mRequestedPreDhcpActionMs > 0) {
- ensureDhcpAction();
- } else {
- sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
- }
- break;
-
- case DhcpClient.CMD_CLEAR_LINKADDRESS:
- mInterfaceCtrl.clearIPv4Address();
- break;
-
- case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
- final LinkAddress ipAddress = (LinkAddress) msg.obj;
- if (mInterfaceCtrl.setIPv4Address(ipAddress)) {
- mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
- } else {
- logError("Failed to set IPv4 address.");
- dispatchCallback(PROV_CHANGE_LOST_PROVISIONING,
- new LinkProperties(mLinkProperties));
- transitionTo(mStoppingState);
- }
- break;
- }
-
- // This message is only received when:
- //
- // a) initial address acquisition succeeds,
- // b) renew succeeds or is NAK'd,
- // c) rebind succeeds or is NAK'd, or
- // c) the lease expires,
- //
- // but never when initial address acquisition fails. The latter
- // condition is now governed by the provisioning timeout.
- case DhcpClient.CMD_POST_DHCP_ACTION:
- stopDhcpAction();
-
- switch (msg.arg1) {
- case DhcpClient.DHCP_SUCCESS:
- handleIPv4Success((DhcpResults) msg.obj);
- break;
- case DhcpClient.DHCP_FAILURE:
- handleIPv4Failure();
- break;
- default:
- logError("Unknown CMD_POST_DHCP_ACTION status: %s", msg.arg1);
- }
- break;
-
- case DhcpClient.CMD_ON_QUIT:
- // DHCPv4 quit early for some reason.
- logError("Unexpected CMD_ON_QUIT.");
- mDhcpClient = null;
- break;
-
- default:
- return NOT_HANDLED;
- }
-
- mMsgStateLogger.handled(this, getCurrentState());
- return HANDLED;
- }
- }
-
- private static class MessageHandlingLogger {
- public String processedInState;
- public String receivedInState;
-
- public void reset() {
- processedInState = null;
- receivedInState = null;
- }
-
- public void handled(State processedIn, IState receivedIn) {
- processedInState = processedIn.getClass().getSimpleName();
- receivedInState = receivedIn.getName();
- }
-
- public String toString() {
- return String.format("rcvd_in=%s, proc_in=%s",
- receivedInState, processedInState);
- }
- }
-
- private static void setNeighborParameters(
- INetd netd, String ifName, int numSolicits, int interSolicitIntervalMs)
- throws RemoteException, IllegalArgumentException {
- Preconditions.checkNotNull(netd);
- Preconditions.checkArgument(!TextUtils.isEmpty(ifName));
- Preconditions.checkArgument(numSolicits > 0);
- Preconditions.checkArgument(interSolicitIntervalMs > 0);
-
- for (int family : new Integer[]{INetd.IPV4, INetd.IPV6}) {
- netd.setProcSysNet(family, INetd.NEIGH, ifName, "retrans_time_ms",
- Integer.toString(interSolicitIntervalMs));
- netd.setProcSysNet(family, INetd.NEIGH, ifName, "ucast_solicit",
- Integer.toString(numSolicits));
- }
- }
-
- // TODO: extract out into CollectionUtils.
- static <T> boolean any(Iterable<T> coll, Predicate<T> fn) {
- for (T t : coll) {
- if (fn.test(t)) {
- return true;
- }
- }
- return false;
- }
-
- static <T> boolean all(Iterable<T> coll, Predicate<T> fn) {
- return !any(coll, not(fn));
- }
-
- static <T> Predicate<T> not(Predicate<T> fn) {
- return (t) -> !fn.test(t);
- }
-
- static <T> String join(String delimiter, Collection<T> coll) {
- return coll.stream().map(Object::toString).collect(Collectors.joining(delimiter));
- }
-
- static <T> T find(Iterable<T> coll, Predicate<T> fn) {
- for (T t: coll) {
- if (fn.test(t)) {
- return t;
- }
- }
- return null;
- }
-
- static <T> List<T> findAll(Collection<T> coll, Predicate<T> fn) {
- return coll.stream().filter(fn).collect(Collectors.toList());
- }
-}
diff --git a/packages/NetworkStack/src/android/net/ip/IpClientLinkObserver.java b/packages/NetworkStack/src/android/net/ip/IpClientLinkObserver.java
deleted file mode 100644
index 8ad99aa0..0000000
--- a/packages/NetworkStack/src/android/net/ip/IpClientLinkObserver.java
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import android.net.InetAddresses;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.RouteInfo;
-import android.util.Log;
-
-import com.android.server.NetworkObserver;
-
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Keeps track of link configuration received from Netd.
- *
- * An instance of this class is constructed by passing in an interface name and a callback. The
- * owner is then responsible for registering the tracker with NetworkObserverRegistry. When the
- * class receives update notifications, it applies the update to its local LinkProperties, and if
- * something has changed, notifies its owner of the update via the callback.
- *
- * The owner can then call {@code getLinkProperties()} in order to find out
- * what changed. If in the meantime the LinkProperties stored here have changed,
- * this class will return the current LinkProperties. Because each change
- * triggers an update callback after the change is made, the owner may get more
- * callbacks than strictly necessary (some of which may be no-ops), but will not
- * be out of sync once all callbacks have been processed.
- *
- * Threading model:
- *
- * - The owner of this class is expected to create it, register it, and call
- * getLinkProperties or clearLinkProperties on its thread.
- * - Most of the methods in the class are implementing NetworkObserver and are called
- * on the handler used to register the observer.
- * - All accesses to mLinkProperties must be synchronized(this). All the other
- * member variables are immutable once the object is constructed.
- *
- * @hide
- */
-public class IpClientLinkObserver implements NetworkObserver {
- private final String mTag;
-
- /**
- * Callback used by {@link IpClientLinkObserver} to send update notifications.
- */
- public interface Callback {
- /**
- * Called when some properties of the link were updated.
- */
- void update();
- }
-
- private final String mInterfaceName;
- private final Callback mCallback;
- private final LinkProperties mLinkProperties;
- private DnsServerRepository mDnsServerRepository;
-
- private static final boolean DBG = false;
-
- public IpClientLinkObserver(String iface, Callback callback) {
- mTag = "NetlinkTracker/" + iface;
- mInterfaceName = iface;
- mCallback = callback;
- mLinkProperties = new LinkProperties();
- mLinkProperties.setInterfaceName(mInterfaceName);
- mDnsServerRepository = new DnsServerRepository();
- }
-
- private void maybeLog(String operation, String iface, LinkAddress address) {
- if (DBG) {
- Log.d(mTag, operation + ": " + address + " on " + iface
- + " flags " + address.getFlags() + " scope " + address.getScope());
- }
- }
-
- private void maybeLog(String operation, Object o) {
- if (DBG) {
- Log.d(mTag, operation + ": " + o.toString());
- }
- }
-
- @Override
- public void onInterfaceRemoved(String iface) {
- maybeLog("interfaceRemoved", iface);
- if (mInterfaceName.equals(iface)) {
- // Our interface was removed. Clear our LinkProperties and tell our owner that they are
- // now empty. Note that from the moment that the interface is removed, any further
- // interface-specific messages (e.g., RTM_DELADDR) will not reach us, because the netd
- // code that parses them will not be able to resolve the ifindex to an interface name.
- clearLinkProperties();
- mCallback.update();
- }
- }
-
- @Override
- public void onInterfaceAddressUpdated(LinkAddress address, String iface) {
- if (mInterfaceName.equals(iface)) {
- maybeLog("addressUpdated", iface, address);
- boolean changed;
- synchronized (this) {
- changed = mLinkProperties.addLinkAddress(address);
- }
- if (changed) {
- mCallback.update();
- }
- }
- }
-
- @Override
- public void onInterfaceAddressRemoved(LinkAddress address, String iface) {
- if (mInterfaceName.equals(iface)) {
- maybeLog("addressRemoved", iface, address);
- boolean changed;
- synchronized (this) {
- changed = mLinkProperties.removeLinkAddress(address);
- }
- if (changed) {
- mCallback.update();
- }
- }
- }
-
- @Override
- public void onRouteUpdated(RouteInfo route) {
- if (mInterfaceName.equals(route.getInterface())) {
- maybeLog("routeUpdated", route);
- boolean changed;
- synchronized (this) {
- changed = mLinkProperties.addRoute(route);
- }
- if (changed) {
- mCallback.update();
- }
- }
- }
-
- @Override
- public void onRouteRemoved(RouteInfo route) {
- if (mInterfaceName.equals(route.getInterface())) {
- maybeLog("routeRemoved", route);
- boolean changed;
- synchronized (this) {
- changed = mLinkProperties.removeRoute(route);
- }
- if (changed) {
- mCallback.update();
- }
- }
- }
-
- @Override
- public void onInterfaceDnsServerInfo(String iface, long lifetime, String[] addresses) {
- if (mInterfaceName.equals(iface)) {
- maybeLog("interfaceDnsServerInfo", Arrays.toString(addresses));
- boolean changed = mDnsServerRepository.addServers(lifetime, addresses);
- if (changed) {
- synchronized (this) {
- mDnsServerRepository.setDnsServersOn(mLinkProperties);
- }
- mCallback.update();
- }
- }
- }
-
- /**
- * Returns a copy of this object's LinkProperties.
- */
- public synchronized LinkProperties getLinkProperties() {
- return new LinkProperties(mLinkProperties);
- }
-
- /**
- * Reset this object's LinkProperties.
- */
- public synchronized void clearLinkProperties() {
- // Clear the repository before clearing mLinkProperties. That way, if a clear() happens
- // while interfaceDnsServerInfo() is being called, we'll end up with no DNS servers in
- // mLinkProperties, as desired.
- mDnsServerRepository = new DnsServerRepository();
- mLinkProperties.clear();
- mLinkProperties.setInterfaceName(mInterfaceName);
- }
-
- /**
- * Tracks DNS server updates received from Netlink.
- *
- * The network may announce an arbitrary number of DNS servers in Router Advertisements at any
- * time. Each announcement has a lifetime; when the lifetime expires, the servers should not be
- * used any more. In this way, the network can gracefully migrate clients from one set of DNS
- * servers to another. Announcements can both raise and lower the lifetime, and an announcement
- * can expire servers by announcing them with a lifetime of zero.
- *
- * Typically the system will only use a small number (2 or 3; {@code NUM_CURRENT_SERVERS}) of
- * DNS servers at any given time. These are referred to as the current servers. In case all the
- * current servers expire, the class also keeps track of a larger (but limited) number of
- * servers that are promoted to current servers when the current ones expire. In order to
- * minimize updates to the rest of the system (and potentially expensive cache flushes) this
- * class attempts to keep the list of current servers constant where possible. More
- * specifically, the list of current servers is only updated if a new server is learned and
- * there are not yet {@code NUM_CURRENT_SERVERS} current servers, or if one or more of the
- * current servers expires or is pushed out of the set. Therefore, the current servers will not
- * necessarily be the ones with the highest lifetime, but the ones learned first.
- *
- * This is by design: if instead the class always preferred the servers with the highest
- * lifetime, a (misconfigured?) network where two or more routers announce more than
- * {@code NUM_CURRENT_SERVERS} unique servers would cause persistent oscillations.
- *
- * TODO: Currently servers are only expired when a new DNS update is received.
- * Update them using timers, or possibly on every notification received by NetlinkTracker.
- *
- * Threading model: run by NetlinkTracker. Methods are synchronized(this) just in case netlink
- * notifications are sent by multiple threads. If future threads use alarms to expire, those
- * alarms must also be synchronized(this).
- *
- */
- private static class DnsServerRepository {
-
- /** How many DNS servers we will use. 3 is suggested by RFC 6106. */
- static final int NUM_CURRENT_SERVERS = 3;
-
- /** How many DNS servers we'll keep track of, in total. */
- static final int NUM_SERVERS = 12;
-
- /** Stores up to {@code NUM_CURRENT_SERVERS} DNS servers we're currently using. */
- private Set<InetAddress> mCurrentServers;
-
- public static final String TAG = "DnsServerRepository";
-
- /**
- * Stores all the DNS servers we know about, for use when the current servers expire.
- * Always sorted in order of decreasing expiry. The elements in this list are also the
- * values of mIndex, and may be elements in mCurrentServers.
- */
- private ArrayList<DnsServerEntry> mAllServers;
-
- /**
- * Indexes the servers so we can update their lifetimes more quickly in the common case
- * where servers are not being added, but only being refreshed.
- */
- private HashMap<InetAddress, DnsServerEntry> mIndex;
-
- DnsServerRepository() {
- mCurrentServers = new HashSet<>();
- mAllServers = new ArrayList<>(NUM_SERVERS);
- mIndex = new HashMap<>(NUM_SERVERS);
- }
-
- /** Sets the DNS servers of the provided LinkProperties object to the current servers. */
- public synchronized void setDnsServersOn(LinkProperties lp) {
- lp.setDnsServers(mCurrentServers);
- }
-
- /**
- * Notifies the class of new DNS server information.
- * @param lifetime the time in seconds that the DNS servers are valid.
- * @param addresses the string representations of the IP addresses of DNS servers to use.
- */
- public synchronized boolean addServers(long lifetime, String[] addresses) {
- // The lifetime is actually an unsigned 32-bit number, but Java doesn't have unsigned.
- // Technically 0xffffffff (the maximum) is special and means "forever", but 2^32 seconds
- // (136 years) is close enough.
- long now = System.currentTimeMillis();
- long expiry = now + 1000 * lifetime;
-
- // Go through the list of servers. For each one, update the entry if one exists, and
- // create one if it doesn't.
- for (String addressString : addresses) {
- InetAddress address;
- try {
- address = InetAddresses.parseNumericAddress(addressString);
- } catch (IllegalArgumentException ex) {
- continue;
- }
-
- if (!updateExistingEntry(address, expiry)) {
- // There was no entry for this server. Create one, unless it's already expired
- // (i.e., if the lifetime is zero; it cannot be < 0 because it's unsigned).
- if (expiry > now) {
- DnsServerEntry entry = new DnsServerEntry(address, expiry);
- mAllServers.add(entry);
- mIndex.put(address, entry);
- }
- }
- }
-
- // Sort the servers by expiry.
- Collections.sort(mAllServers);
-
- // Prune excess entries and update the current server list.
- return updateCurrentServers();
- }
-
- private synchronized boolean updateExistingEntry(InetAddress address, long expiry) {
- DnsServerEntry existing = mIndex.get(address);
- if (existing != null) {
- existing.expiry = expiry;
- return true;
- }
- return false;
- }
-
- private synchronized boolean updateCurrentServers() {
- long now = System.currentTimeMillis();
- boolean changed = false;
-
- // Prune excess or expired entries.
- for (int i = mAllServers.size() - 1; i >= 0; i--) {
- if (i >= NUM_SERVERS || mAllServers.get(i).expiry < now) {
- DnsServerEntry removed = mAllServers.remove(i);
- mIndex.remove(removed.address);
- changed |= mCurrentServers.remove(removed.address);
- } else {
- break;
- }
- }
-
- // Add servers to the current set, in order of decreasing lifetime, until it has enough.
- // Prefer existing servers over new servers in order to minimize updates to the rest of
- // the system and avoid persistent oscillations.
- for (DnsServerEntry entry : mAllServers) {
- if (mCurrentServers.size() < NUM_CURRENT_SERVERS) {
- changed |= mCurrentServers.add(entry.address);
- } else {
- break;
- }
- }
- return changed;
- }
- }
-
- /**
- * Represents a DNS server entry with an expiry time.
- *
- * Implements Comparable so DNS server entries can be sorted by lifetime, longest-lived first.
- * The ordering of entries with the same lifetime is unspecified, because given two servers with
- * identical lifetimes, we don't care which one we use, and only comparing the lifetime is much
- * faster than comparing the IP address as well.
- *
- * Note: this class has a natural ordering that is inconsistent with equals.
- */
- private static class DnsServerEntry implements Comparable<DnsServerEntry> {
- /** The IP address of the DNS server. */
- public final InetAddress address;
- /** The time until which the DNS server may be used. A Java millisecond time as might be
- * returned by currentTimeMillis(). */
- public long expiry;
-
- DnsServerEntry(InetAddress address, long expiry) throws IllegalArgumentException {
- this.address = address;
- this.expiry = expiry;
- }
-
- public int compareTo(DnsServerEntry other) {
- return Long.compare(other.expiry, this.expiry);
- }
- }
-}
diff --git a/packages/NetworkStack/src/android/net/ip/IpNeighborMonitor.java b/packages/NetworkStack/src/android/net/ip/IpNeighborMonitor.java
deleted file mode 100644
index 6ae9a2b..0000000
--- a/packages/NetworkStack/src/android/net/ip/IpNeighborMonitor.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
-import static android.net.netlink.NetlinkConstants.hexify;
-import static android.net.netlink.NetlinkConstants.stringForNlMsgType;
-import static android.net.util.SocketUtils.makeNetlinkSocketAddress;
-import static android.system.OsConstants.AF_NETLINK;
-import static android.system.OsConstants.NETLINK_ROUTE;
-import static android.system.OsConstants.SOCK_DGRAM;
-import static android.system.OsConstants.SOCK_NONBLOCK;
-
-import android.net.MacAddress;
-import android.net.netlink.NetlinkErrorMessage;
-import android.net.netlink.NetlinkMessage;
-import android.net.netlink.NetlinkSocket;
-import android.net.netlink.RtNetlinkNeighborMessage;
-import android.net.netlink.StructNdMsg;
-import android.net.util.NetworkStackUtils;
-import android.net.util.PacketReader;
-import android.net.util.SharedLog;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-import android.util.Log;
-
-import java.io.FileDescriptor;
-import java.net.InetAddress;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.StringJoiner;
-
-
-/**
- * IpNeighborMonitor.
- *
- * Monitors the kernel rtnetlink neighbor notifications and presents to callers
- * NeighborEvents describing each event. Callers can provide a consumer instance
- * to both filter (e.g. by interface index and IP address) and handle the
- * generated NeighborEvents.
- *
- * @hide
- */
-public class IpNeighborMonitor extends PacketReader {
- private static final String TAG = IpNeighborMonitor.class.getSimpleName();
- private static final boolean DBG = false;
- private static final boolean VDBG = false;
-
- /**
- * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
- * for the given IP address on the specified interface index.
- *
- * @return 0 if the request was successfully passed to the kernel; otherwise return
- * a non-zero error code.
- */
- public static int startKernelNeighborProbe(int ifIndex, InetAddress ip) {
- final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex;
- if (DBG) { Log.d(TAG, msgSnippet); }
-
- final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
- 1, ip, StructNdMsg.NUD_PROBE, ifIndex, null);
-
- try {
- NetlinkSocket.sendOneShotKernelMessage(NETLINK_ROUTE, msg);
- } catch (ErrnoException e) {
- Log.e(TAG, "Error " + msgSnippet + ": " + e);
- return -e.errno;
- }
-
- return 0;
- }
-
- public static class NeighborEvent {
- final long elapsedMs;
- final short msgType;
- final int ifindex;
- final InetAddress ip;
- final short nudState;
- final MacAddress macAddr;
-
- public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip,
- short nudState, MacAddress macAddr) {
- this.elapsedMs = elapsedMs;
- this.msgType = msgType;
- this.ifindex = ifindex;
- this.ip = ip;
- this.nudState = nudState;
- this.macAddr = macAddr;
- }
-
- boolean isConnected() {
- return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateConnected(nudState);
- }
-
- boolean isValid() {
- return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateValid(nudState);
- }
-
- @Override
- public String toString() {
- final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}");
- return j.add("@" + elapsedMs)
- .add(stringForNlMsgType(msgType))
- .add("if=" + ifindex)
- .add(ip.getHostAddress())
- .add(StructNdMsg.stringForNudState(nudState))
- .add("[" + macAddr + "]")
- .toString();
- }
- }
-
- public interface NeighborEventConsumer {
- // Every neighbor event received on the netlink socket is passed in
- // here. Subclasses should filter for events of interest.
- public void accept(NeighborEvent event);
- }
-
- private final SharedLog mLog;
- private final NeighborEventConsumer mConsumer;
-
- public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) {
- super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE);
- mLog = log.forSubComponent(TAG);
- mConsumer = (cb != null) ? cb : (event) -> { /* discard */ };
- }
-
- @Override
- protected FileDescriptor createFd() {
- FileDescriptor fd = null;
-
- try {
- fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, NETLINK_ROUTE);
- Os.bind(fd, makeNetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH));
- NetlinkSocket.connectToKernel(fd);
-
- if (VDBG) {
- final SocketAddress nlAddr = Os.getsockname(fd);
- Log.d(TAG, "bound to sockaddr_nl{" + nlAddr.toString() + "}");
- }
- } catch (ErrnoException|SocketException e) {
- logError("Failed to create rtnetlink socket", e);
- NetworkStackUtils.closeSocketQuietly(fd);
- return null;
- }
-
- return fd;
- }
-
- @Override
- protected void handlePacket(byte[] recvbuf, int length) {
- final long whenMs = SystemClock.elapsedRealtime();
-
- final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length);
- byteBuffer.order(ByteOrder.nativeOrder());
-
- parseNetlinkMessageBuffer(byteBuffer, whenMs);
- }
-
- private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) {
- while (byteBuffer.remaining() > 0) {
- final int position = byteBuffer.position();
- final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer);
- if (nlMsg == null || nlMsg.getHeader() == null) {
- byteBuffer.position(position);
- mLog.e("unparsable netlink msg: " + hexify(byteBuffer));
- break;
- }
-
- final int srcPortId = nlMsg.getHeader().nlmsg_pid;
- if (srcPortId != 0) {
- mLog.e("non-kernel source portId: " + Integer.toUnsignedLong(srcPortId));
- break;
- }
-
- if (nlMsg instanceof NetlinkErrorMessage) {
- mLog.e("netlink error: " + nlMsg);
- continue;
- } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) {
- mLog.i("non-rtnetlink neighbor msg: " + nlMsg);
- continue;
- }
-
- evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs);
- }
- }
-
- private void evaluateRtNetlinkNeighborMessage(
- RtNetlinkNeighborMessage neighMsg, long whenMs) {
- final short msgType = neighMsg.getHeader().nlmsg_type;
- final StructNdMsg ndMsg = neighMsg.getNdHeader();
- if (ndMsg == null) {
- mLog.e("RtNetlinkNeighborMessage without ND message header!");
- return;
- }
-
- final int ifindex = ndMsg.ndm_ifindex;
- final InetAddress destination = neighMsg.getDestination();
- final short nudState =
- (msgType == RTM_DELNEIGH)
- ? StructNdMsg.NUD_NONE
- : ndMsg.ndm_state;
-
- final NeighborEvent event = new NeighborEvent(
- whenMs, msgType, ifindex, destination, nudState,
- getMacAddress(neighMsg.getLinkLayerAddress()));
-
- if (VDBG) {
- Log.d(TAG, neighMsg.toString());
- }
- if (DBG) {
- Log.d(TAG, event.toString());
- }
-
- mConsumer.accept(event);
- }
-
- private static MacAddress getMacAddress(byte[] linkLayerAddress) {
- if (linkLayerAddress != null) {
- try {
- return MacAddress.fromBytes(linkLayerAddress);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Failed to parse link-layer address: " + hexify(linkLayerAddress));
- }
- }
-
- return null;
- }
-}
diff --git a/packages/NetworkStack/src/android/net/ip/IpReachabilityMonitor.java b/packages/NetworkStack/src/android/net/ip/IpReachabilityMonitor.java
deleted file mode 100644
index c19a24e..0000000
--- a/packages/NetworkStack/src/android/net/ip/IpReachabilityMonitor.java
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import static android.net.metrics.IpReachabilityEvent.NUD_FAILED;
-import static android.net.metrics.IpReachabilityEvent.NUD_FAILED_ORGANIC;
-import static android.net.metrics.IpReachabilityEvent.PROVISIONING_LOST;
-import static android.net.metrics.IpReachabilityEvent.PROVISIONING_LOST_ORGANIC;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.LinkProperties;
-import android.net.RouteInfo;
-import android.net.ip.IpNeighborMonitor.NeighborEvent;
-import android.net.metrics.IpConnectivityLog;
-import android.net.metrics.IpReachabilityEvent;
-import android.net.netlink.StructNdMsg;
-import android.net.util.InterfaceParams;
-import android.net.util.SharedLog;
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.PrintWriter;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-
-/**
- * IpReachabilityMonitor.
- *
- * Monitors on-link IP reachability and notifies callers whenever any on-link
- * addresses of interest appear to have become unresponsive.
- *
- * This code does not concern itself with "why" a neighbour might have become
- * unreachable. Instead, it primarily reacts to the kernel's notion of IP
- * reachability for each of the neighbours we know to be critically important
- * to normal network connectivity. As such, it is often "just the messenger":
- * the neighbours about which it warns are already deemed by the kernel to have
- * become unreachable.
- *
- *
- * How it works:
- *
- * 1. The "on-link neighbours of interest" found in a given LinkProperties
- * instance are added to a "watch list" via #updateLinkProperties().
- * This usually means all default gateways and any on-link DNS servers.
- *
- * 2. We listen continuously for netlink neighbour messages (RTM_NEWNEIGH,
- * RTM_DELNEIGH), watching only for neighbours in the watch list.
- *
- * - A neighbour going into NUD_REACHABLE, NUD_STALE, NUD_DELAY, and
- * even NUD_PROBE is perfectly normal; we merely record the new state.
- *
- * - A neighbour's entry may be deleted (RTM_DELNEIGH), for example due
- * to garbage collection. This is not necessarily of immediate
- * concern; we record the neighbour as moving to NUD_NONE.
- *
- * - A neighbour transitioning to NUD_FAILED (for any reason) is
- * critically important and is handled as described below in #4.
- *
- * 3. All on-link neighbours in the watch list can be forcibly "probed" by
- * calling #probeAll(). This should be called whenever it is important to
- * verify that critical neighbours on the link are still reachable, e.g.
- * when roaming between BSSIDs.
- *
- * - The kernel will send unicast ARP requests for IPv4 neighbours and
- * unicast NS packets for IPv6 neighbours. The expected replies will
- * likely be unicast.
- *
- * - The forced probing is done holding a wakelock. The kernel may,
- * however, initiate probing of a neighbor on its own, i.e. whenever
- * a neighbour has expired from NUD_DELAY.
- *
- * - The kernel sends:
- *
- * /proc/sys/net/ipv{4,6}/neigh/<ifname>/ucast_solicit
- *
- * number of probes (usually 3) every:
- *
- * /proc/sys/net/ipv{4,6}/neigh/<ifname>/retrans_time_ms
- *
- * number of milliseconds (usually 1000ms). This normally results in
- * 3 unicast packets, 1 per second.
- *
- * - If no response is received to any of the probe packets, the kernel
- * marks the neighbour as being in state NUD_FAILED, and the listening
- * process in #2 will learn of it.
- *
- * 4. We call the supplied Callback#notifyLost() function if the loss of a
- * neighbour in NUD_FAILED would cause IPv4 or IPv6 configuration to
- * become incomplete (a loss of provisioning).
- *
- * - For example, losing all our IPv4 on-link DNS servers (or losing
- * our only IPv6 default gateway) constitutes a loss of IPv4 (IPv6)
- * provisioning; Callback#notifyLost() would be called.
- *
- * - Since it can be non-trivial to reacquire certain IP provisioning
- * state it may be best for the link to disconnect completely and
- * reconnect afresh.
- *
- * Accessing an instance of this class from multiple threads is NOT safe.
- *
- * @hide
- */
-public class IpReachabilityMonitor {
- private static final String TAG = "IpReachabilityMonitor";
- private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
-
- public interface Callback {
- // This callback function must execute as quickly as possible as it is
- // run on the same thread that listens to kernel neighbor updates.
- //
- // TODO: refactor to something like notifyProvisioningLost(String msg).
- public void notifyLost(InetAddress ip, String logMsg);
- }
-
- /**
- * Encapsulates IpReachabilityMonitor depencencies on systems that hinder unit testing.
- * TODO: consider also wrapping MultinetworkPolicyTracker in this interface.
- */
- interface Dependencies {
- void acquireWakeLock(long durationMs);
-
- static Dependencies makeDefault(Context context, String iface) {
- final String lockName = TAG + "." + iface;
- final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- final WakeLock lock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, lockName);
-
- return new Dependencies() {
- public void acquireWakeLock(long durationMs) {
- lock.acquire(durationMs);
- }
- };
- }
- }
-
- private final InterfaceParams mInterfaceParams;
- private final IpNeighborMonitor mIpNeighborMonitor;
- private final SharedLog mLog;
- private final Callback mCallback;
- private final Dependencies mDependencies;
- private final boolean mUsingMultinetworkPolicyTracker;
- private final ConnectivityManager mCm;
- private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
- private LinkProperties mLinkProperties = new LinkProperties();
- private Map<InetAddress, NeighborEvent> mNeighborWatchList = new HashMap<>();
- // Time in milliseconds of the last forced probe request.
- private volatile long mLastProbeTimeMs;
-
- public IpReachabilityMonitor(
- Context context, InterfaceParams ifParams, Handler h, SharedLog log, Callback callback,
- boolean usingMultinetworkPolicyTracker) {
- this(context, ifParams, h, log, callback, usingMultinetworkPolicyTracker,
- Dependencies.makeDefault(context, ifParams.name));
- }
-
- @VisibleForTesting
- IpReachabilityMonitor(Context context, InterfaceParams ifParams, Handler h, SharedLog log,
- Callback callback, boolean usingMultinetworkPolicyTracker, Dependencies dependencies) {
- if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams");
-
- mInterfaceParams = ifParams;
- mLog = log.forSubComponent(TAG);
- mCallback = callback;
- mUsingMultinetworkPolicyTracker = usingMultinetworkPolicyTracker;
- mCm = context.getSystemService(ConnectivityManager.class);
- mDependencies = dependencies;
-
- mIpNeighborMonitor = new IpNeighborMonitor(h, mLog,
- (NeighborEvent event) -> {
- if (mInterfaceParams.index != event.ifindex) return;
- if (!mNeighborWatchList.containsKey(event.ip)) return;
-
- final NeighborEvent prev = mNeighborWatchList.put(event.ip, event);
-
- // TODO: Consider what to do with other states that are not within
- // NeighborEvent#isValid() (i.e. NUD_NONE, NUD_INCOMPLETE).
- if (event.nudState == StructNdMsg.NUD_FAILED) {
- mLog.w("ALERT neighbor went from: " + prev + " to: " + event);
- handleNeighborLost(event);
- }
- });
- mIpNeighborMonitor.start();
- }
-
- public void stop() {
- mIpNeighborMonitor.stop();
- clearLinkProperties();
- }
-
- public void dump(PrintWriter pw) {
- if (Looper.myLooper() == mIpNeighborMonitor.getHandler().getLooper()) {
- pw.println(describeWatchList("\n"));
- return;
- }
-
- final ConditionVariable cv = new ConditionVariable(false);
- mIpNeighborMonitor.getHandler().post(() -> {
- pw.println(describeWatchList("\n"));
- cv.open();
- });
-
- if (!cv.block(1000)) {
- pw.println("Timed out waiting for IpReachabilityMonitor dump");
- }
- }
-
- private String describeWatchList() { return describeWatchList(" "); }
-
- private String describeWatchList(String sep) {
- final StringBuilder sb = new StringBuilder();
- sb.append("iface{" + mInterfaceParams + "}," + sep);
- sb.append("ntable=[" + sep);
- String delimiter = "";
- for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) {
- sb.append(delimiter).append(entry.getKey().getHostAddress() + "/" + entry.getValue());
- delimiter = "," + sep;
- }
- sb.append("]");
- return sb.toString();
- }
-
- private static boolean isOnLink(List<RouteInfo> routes, InetAddress ip) {
- for (RouteInfo route : routes) {
- if (!route.hasGateway() && route.matches(ip)) {
- return true;
- }
- }
- return false;
- }
-
- public void updateLinkProperties(LinkProperties lp) {
- if (!mInterfaceParams.name.equals(lp.getInterfaceName())) {
- // TODO: figure out whether / how to cope with interface changes.
- Log.wtf(TAG, "requested LinkProperties interface '" + lp.getInterfaceName() +
- "' does not match: " + mInterfaceParams.name);
- return;
- }
-
- mLinkProperties = new LinkProperties(lp);
- Map<InetAddress, NeighborEvent> newNeighborWatchList = new HashMap<>();
-
- final List<RouteInfo> routes = mLinkProperties.getRoutes();
- for (RouteInfo route : routes) {
- if (route.hasGateway()) {
- InetAddress gw = route.getGateway();
- if (isOnLink(routes, gw)) {
- newNeighborWatchList.put(gw, mNeighborWatchList.getOrDefault(gw, null));
- }
- }
- }
-
- for (InetAddress dns : lp.getDnsServers()) {
- if (isOnLink(routes, dns)) {
- newNeighborWatchList.put(dns, mNeighborWatchList.getOrDefault(dns, null));
- }
- }
-
- mNeighborWatchList = newNeighborWatchList;
- if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); }
- }
-
- public void clearLinkProperties() {
- mLinkProperties.clear();
- mNeighborWatchList.clear();
- if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); }
- }
-
- private void handleNeighborLost(NeighborEvent event) {
- final LinkProperties whatIfLp = new LinkProperties(mLinkProperties);
-
- InetAddress ip = null;
- for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) {
- // TODO: Consider using NeighborEvent#isValid() here; it's more
- // strict but may interact badly if other entries are somehow in
- // NUD_INCOMPLETE (say, during network attach).
- if (entry.getValue().nudState != StructNdMsg.NUD_FAILED) continue;
-
- ip = entry.getKey();
- for (RouteInfo route : mLinkProperties.getRoutes()) {
- if (ip.equals(route.getGateway())) {
- whatIfLp.removeRoute(route);
- }
- }
-
- if (avoidingBadLinks() || !(ip instanceof Inet6Address)) {
- // We should do this unconditionally, but alas we cannot: b/31827713.
- whatIfLp.removeDnsServer(ip);
- }
- }
-
- final boolean lostProvisioning =
- (mLinkProperties.isIpv4Provisioned() && !whatIfLp.isIpv4Provisioned())
- || (mLinkProperties.isIpv6Provisioned() && !whatIfLp.isIpv6Provisioned());
-
- if (lostProvisioning) {
- final String logMsg = "FAILURE: LOST_PROVISIONING, " + event;
- Log.w(TAG, logMsg);
- if (mCallback != null) {
- // TODO: remove |ip| when the callback signature no longer has
- // an InetAddress argument.
- mCallback.notifyLost(ip, logMsg);
- }
- }
- logNudFailed(lostProvisioning);
- }
-
- private boolean avoidingBadLinks() {
- return !mUsingMultinetworkPolicyTracker || mCm.shouldAvoidBadWifi();
- }
-
- public void probeAll() {
- final List<InetAddress> ipProbeList = new ArrayList<>(mNeighborWatchList.keySet());
-
- if (!ipProbeList.isEmpty()) {
- // Keep the CPU awake long enough to allow all ARP/ND
- // probes a reasonable chance at success. See b/23197666.
- //
- // The wakelock we use is (by default) refcounted, and this version
- // of acquire(timeout) queues a release message to keep acquisitions
- // and releases balanced.
- mDependencies.acquireWakeLock(getProbeWakeLockDuration());
- }
-
- for (InetAddress ip : ipProbeList) {
- final int rval = IpNeighborMonitor.startKernelNeighborProbe(mInterfaceParams.index, ip);
- mLog.log(String.format("put neighbor %s into NUD_PROBE state (rval=%d)",
- ip.getHostAddress(), rval));
- logEvent(IpReachabilityEvent.PROBE, rval);
- }
- mLastProbeTimeMs = SystemClock.elapsedRealtime();
- }
-
- private static long getProbeWakeLockDuration() {
- // Ideally, this would be computed by examining the values of:
- //
- // /proc/sys/net/ipv[46]/neigh/<ifname>/ucast_solicit
- //
- // and:
- //
- // /proc/sys/net/ipv[46]/neigh/<ifname>/retrans_time_ms
- //
- // For now, just make some assumptions.
- final long numUnicastProbes = 3;
- final long retransTimeMs = 1000;
- final long gracePeriodMs = 500;
- return (numUnicastProbes * retransTimeMs) + gracePeriodMs;
- }
-
- private void logEvent(int probeType, int errorCode) {
- int eventType = probeType | (errorCode & 0xff);
- mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
- }
-
- private void logNudFailed(boolean lostProvisioning) {
- long duration = SystemClock.elapsedRealtime() - mLastProbeTimeMs;
- boolean isFromProbe = (duration < getProbeWakeLockDuration());
- int eventType = nudFailureEventType(isFromProbe, lostProvisioning);
- mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
- }
-
- /**
- * Returns the NUD failure event type code corresponding to the given conditions.
- */
- private static int nudFailureEventType(boolean isFromProbe, boolean isProvisioningLost) {
- if (isFromProbe) {
- return isProvisioningLost ? PROVISIONING_LOST : NUD_FAILED;
- } else {
- return isProvisioningLost ? PROVISIONING_LOST_ORGANIC : NUD_FAILED_ORGANIC;
- }
- }
-}
diff --git a/packages/NetworkStack/src/android/net/util/ConnectivityPacketSummary.java b/packages/NetworkStack/src/android/net/util/ConnectivityPacketSummary.java
deleted file mode 100644
index 08c3f60..0000000
--- a/packages/NetworkStack/src/android/net/util/ConnectivityPacketSummary.java
+++ /dev/null
@@ -1,435 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import static android.system.OsConstants.IPPROTO_ICMPV6;
-import static android.system.OsConstants.IPPROTO_UDP;
-
-import static com.android.server.util.NetworkStackConstants.ARP_HWTYPE_ETHER;
-import static com.android.server.util.NetworkStackConstants.ARP_PAYLOAD_LEN;
-import static com.android.server.util.NetworkStackConstants.ARP_REPLY;
-import static com.android.server.util.NetworkStackConstants.ARP_REQUEST;
-import static com.android.server.util.NetworkStackConstants.DHCP4_CLIENT_PORT;
-import static com.android.server.util.NetworkStackConstants.ETHER_ADDR_LEN;
-import static com.android.server.util.NetworkStackConstants.ETHER_DST_ADDR_OFFSET;
-import static com.android.server.util.NetworkStackConstants.ETHER_HEADER_LEN;
-import static com.android.server.util.NetworkStackConstants.ETHER_SRC_ADDR_OFFSET;
-import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_ARP;
-import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_IPV4;
-import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_IPV6;
-import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_OFFSET;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_HEADER_MIN_LEN;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_MIN_LENGTH;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_MTU;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_TLLA;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_NEIGHBOR_SOLICITATION;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION;
-import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_LEN;
-import static com.android.server.util.NetworkStackConstants.IPV4_DST_ADDR_OFFSET;
-import static com.android.server.util.NetworkStackConstants.IPV4_FLAGS_OFFSET;
-import static com.android.server.util.NetworkStackConstants.IPV4_FRAGMENT_MASK;
-import static com.android.server.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN;
-import static com.android.server.util.NetworkStackConstants.IPV4_IHL_MASK;
-import static com.android.server.util.NetworkStackConstants.IPV4_PROTOCOL_OFFSET;
-import static com.android.server.util.NetworkStackConstants.IPV4_SRC_ADDR_OFFSET;
-import static com.android.server.util.NetworkStackConstants.IPV6_ADDR_LEN;
-import static com.android.server.util.NetworkStackConstants.IPV6_HEADER_LEN;
-import static com.android.server.util.NetworkStackConstants.IPV6_PROTOCOL_OFFSET;
-import static com.android.server.util.NetworkStackConstants.IPV6_SRC_ADDR_OFFSET;
-import static com.android.server.util.NetworkStackConstants.UDP_HEADER_LEN;
-
-import android.net.MacAddress;
-import android.net.dhcp.DhcpPacket;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.StringJoiner;
-
-
-/**
- * Critical connectivity packet summarizing class.
- *
- * Outputs short descriptions of ARP, DHCPv4, and IPv6 RS/RA/NS/NA packets.
- *
- * @hide
- */
-public class ConnectivityPacketSummary {
- private static final String TAG = ConnectivityPacketSummary.class.getSimpleName();
-
- private final byte[] mHwAddr;
- private final byte[] mBytes;
- private final int mLength;
- private final ByteBuffer mPacket;
- private final String mSummary;
-
- public static String summarize(MacAddress hwaddr, byte[] buffer) {
- return summarize(hwaddr, buffer, buffer.length);
- }
-
- // Methods called herein perform some but by no means all error checking.
- // They may throw runtime exceptions on malformed packets.
- public static String summarize(MacAddress macAddr, byte[] buffer, int length) {
- if ((macAddr == null) || (buffer == null)) return null;
- length = Math.min(length, buffer.length);
- return (new ConnectivityPacketSummary(macAddr, buffer, length)).toString();
- }
-
- private ConnectivityPacketSummary(MacAddress macAddr, byte[] buffer, int length) {
- mHwAddr = macAddr.toByteArray();
- mBytes = buffer;
- mLength = Math.min(length, mBytes.length);
- mPacket = ByteBuffer.wrap(mBytes, 0, mLength);
- mPacket.order(ByteOrder.BIG_ENDIAN);
-
- final StringJoiner sj = new StringJoiner(" ");
- // TODO: support other link-layers, or even no link-layer header.
- parseEther(sj);
- mSummary = sj.toString();
- }
-
- public String toString() {
- return mSummary;
- }
-
- private void parseEther(StringJoiner sj) {
- if (mPacket.remaining() < ETHER_HEADER_LEN) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- mPacket.position(ETHER_SRC_ADDR_OFFSET);
- final ByteBuffer srcMac = (ByteBuffer) mPacket.slice().limit(ETHER_ADDR_LEN);
- sj.add(ByteBuffer.wrap(mHwAddr).equals(srcMac) ? "TX" : "RX");
- sj.add(getMacAddressString(srcMac));
-
- mPacket.position(ETHER_DST_ADDR_OFFSET);
- final ByteBuffer dstMac = (ByteBuffer) mPacket.slice().limit(ETHER_ADDR_LEN);
- sj.add(">").add(getMacAddressString(dstMac));
-
- mPacket.position(ETHER_TYPE_OFFSET);
- final int etherType = asUint(mPacket.getShort());
- switch (etherType) {
- case ETHER_TYPE_ARP:
- sj.add("arp");
- parseARP(sj);
- break;
- case ETHER_TYPE_IPV4:
- sj.add("ipv4");
- parseIPv4(sj);
- break;
- case ETHER_TYPE_IPV6:
- sj.add("ipv6");
- parseIPv6(sj);
- break;
- default:
- // Unknown ether type.
- sj.add("ethtype").add(asString(etherType));
- break;
- }
- }
-
- private void parseARP(StringJoiner sj) {
- if (mPacket.remaining() < ARP_PAYLOAD_LEN) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- if (asUint(mPacket.getShort()) != ARP_HWTYPE_ETHER ||
- asUint(mPacket.getShort()) != ETHER_TYPE_IPV4 ||
- asUint(mPacket.get()) != ETHER_ADDR_LEN ||
- asUint(mPacket.get()) != IPV4_ADDR_LEN) {
- sj.add("unexpected header");
- return;
- }
-
- final int opCode = asUint(mPacket.getShort());
-
- final String senderHwAddr = getMacAddressString(mPacket);
- final String senderIPv4 = getIPv4AddressString(mPacket);
- getMacAddressString(mPacket); // target hardware address, unused
- final String targetIPv4 = getIPv4AddressString(mPacket);
-
- if (opCode == ARP_REQUEST) {
- sj.add("who-has").add(targetIPv4);
- } else if (opCode == ARP_REPLY) {
- sj.add("reply").add(senderIPv4).add(senderHwAddr);
- } else {
- sj.add("unknown opcode").add(asString(opCode));
- }
- }
-
- private void parseIPv4(StringJoiner sj) {
- if (!mPacket.hasRemaining()) {
- sj.add("runt");
- return;
- }
-
- final int startOfIpLayer = mPacket.position();
- final int ipv4HeaderLength = (mPacket.get(startOfIpLayer) & IPV4_IHL_MASK) * 4;
- if (mPacket.remaining() < ipv4HeaderLength ||
- mPacket.remaining() < IPV4_HEADER_MIN_LEN) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
- final int startOfTransportLayer = startOfIpLayer + ipv4HeaderLength;
-
- mPacket.position(startOfIpLayer + IPV4_FLAGS_OFFSET);
- final int flagsAndFragment = asUint(mPacket.getShort());
- final boolean isFragment = (flagsAndFragment & IPV4_FRAGMENT_MASK) != 0;
-
- mPacket.position(startOfIpLayer + IPV4_PROTOCOL_OFFSET);
- final int protocol = asUint(mPacket.get());
-
- mPacket.position(startOfIpLayer + IPV4_SRC_ADDR_OFFSET);
- final String srcAddr = getIPv4AddressString(mPacket);
-
- mPacket.position(startOfIpLayer + IPV4_DST_ADDR_OFFSET);
- final String dstAddr = getIPv4AddressString(mPacket);
-
- sj.add(srcAddr).add(">").add(dstAddr);
-
- mPacket.position(startOfTransportLayer);
- if (protocol == IPPROTO_UDP) {
- sj.add("udp");
- if (isFragment) sj.add("fragment");
- else parseUDP(sj);
- } else {
- sj.add("proto").add(asString(protocol));
- if (isFragment) sj.add("fragment");
- }
- }
-
- private void parseIPv6(StringJoiner sj) {
- if (mPacket.remaining() < IPV6_HEADER_LEN) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- final int startOfIpLayer = mPacket.position();
-
- mPacket.position(startOfIpLayer + IPV6_PROTOCOL_OFFSET);
- final int protocol = asUint(mPacket.get());
-
- mPacket.position(startOfIpLayer + IPV6_SRC_ADDR_OFFSET);
- final String srcAddr = getIPv6AddressString(mPacket);
- final String dstAddr = getIPv6AddressString(mPacket);
-
- sj.add(srcAddr).add(">").add(dstAddr);
-
- mPacket.position(startOfIpLayer + IPV6_HEADER_LEN);
- if (protocol == IPPROTO_ICMPV6) {
- sj.add("icmp6");
- parseICMPv6(sj);
- } else {
- sj.add("proto").add(asString(protocol));
- }
- }
-
- private void parseICMPv6(StringJoiner sj) {
- if (mPacket.remaining() < ICMPV6_HEADER_MIN_LEN) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- final int icmp6Type = asUint(mPacket.get());
- final int icmp6Code = asUint(mPacket.get());
- mPacket.getShort(); // checksum, unused
-
- switch (icmp6Type) {
- case ICMPV6_ROUTER_SOLICITATION:
- sj.add("rs");
- parseICMPv6RouterSolicitation(sj);
- break;
- case ICMPV6_ROUTER_ADVERTISEMENT:
- sj.add("ra");
- parseICMPv6RouterAdvertisement(sj);
- break;
- case ICMPV6_NEIGHBOR_SOLICITATION:
- sj.add("ns");
- parseICMPv6NeighborMessage(sj);
- break;
- case ICMPV6_NEIGHBOR_ADVERTISEMENT:
- sj.add("na");
- parseICMPv6NeighborMessage(sj);
- break;
- default:
- sj.add("type").add(asString(icmp6Type));
- sj.add("code").add(asString(icmp6Code));
- break;
- }
- }
-
- private void parseICMPv6RouterSolicitation(StringJoiner sj) {
- final int RESERVED = 4;
- if (mPacket.remaining() < RESERVED) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- mPacket.position(mPacket.position() + RESERVED);
- parseICMPv6NeighborDiscoveryOptions(sj);
- }
-
- private void parseICMPv6RouterAdvertisement(StringJoiner sj) {
- final int FLAGS_AND_TIMERS = 3 * 4;
- if (mPacket.remaining() < FLAGS_AND_TIMERS) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- mPacket.position(mPacket.position() + FLAGS_AND_TIMERS);
- parseICMPv6NeighborDiscoveryOptions(sj);
- }
-
- private void parseICMPv6NeighborMessage(StringJoiner sj) {
- final int RESERVED = 4;
- final int minReq = RESERVED + IPV6_ADDR_LEN;
- if (mPacket.remaining() < minReq) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- mPacket.position(mPacket.position() + RESERVED);
- sj.add(getIPv6AddressString(mPacket));
- parseICMPv6NeighborDiscoveryOptions(sj);
- }
-
- private void parseICMPv6NeighborDiscoveryOptions(StringJoiner sj) {
- // All ND options are TLV, where T is one byte and L is one byte equal
- // to the length of T + L + V in units of 8 octets.
- while (mPacket.remaining() >= ICMPV6_ND_OPTION_MIN_LENGTH) {
- final int ndType = asUint(mPacket.get());
- final int ndLength = asUint(mPacket.get());
- final int ndBytes = ndLength * ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR - 2;
- if (ndBytes < 0 || ndBytes > mPacket.remaining()) {
- sj.add("<malformed>");
- break;
- }
- final int position = mPacket.position();
-
- switch (ndType) {
- case ICMPV6_ND_OPTION_SLLA:
- sj.add("slla");
- sj.add(getMacAddressString(mPacket));
- break;
- case ICMPV6_ND_OPTION_TLLA:
- sj.add("tlla");
- sj.add(getMacAddressString(mPacket));
- break;
- case ICMPV6_ND_OPTION_MTU:
- sj.add("mtu");
- final short reserved = mPacket.getShort();
- sj.add(asString(mPacket.getInt()));
- break;
- default:
- // Skip.
- break;
- }
-
- mPacket.position(position + ndBytes);
- }
- }
-
- private void parseUDP(StringJoiner sj) {
- if (mPacket.remaining() < UDP_HEADER_LEN) {
- sj.add("runt:").add(asString(mPacket.remaining()));
- return;
- }
-
- final int previous = mPacket.position();
- final int srcPort = asUint(mPacket.getShort());
- final int dstPort = asUint(mPacket.getShort());
- sj.add(asString(srcPort)).add(">").add(asString(dstPort));
-
- mPacket.position(previous + UDP_HEADER_LEN);
- if (srcPort == DHCP4_CLIENT_PORT || dstPort == DHCP4_CLIENT_PORT) {
- sj.add("dhcp4");
- parseDHCPv4(sj);
- }
- }
-
- private void parseDHCPv4(StringJoiner sj) {
- final DhcpPacket dhcpPacket;
- try {
- dhcpPacket = DhcpPacket.decodeFullPacket(mBytes, mLength, DhcpPacket.ENCAP_L2);
- sj.add(dhcpPacket.toString());
- } catch (DhcpPacket.ParseException e) {
- sj.add("parse error: " + e);
- }
- }
-
- private static String getIPv4AddressString(ByteBuffer ipv4) {
- return getIpAddressString(ipv4, IPV4_ADDR_LEN);
- }
-
- private static String getIPv6AddressString(ByteBuffer ipv6) {
- return getIpAddressString(ipv6, IPV6_ADDR_LEN);
- }
-
- private static String getIpAddressString(ByteBuffer ip, int byteLength) {
- if (ip == null || ip.remaining() < byteLength) return "invalid";
-
- byte[] bytes = new byte[byteLength];
- ip.get(bytes, 0, byteLength);
- try {
- InetAddress addr = InetAddress.getByAddress(bytes);
- return addr.getHostAddress();
- } catch (UnknownHostException uhe) {
- return "unknown";
- }
- }
-
- private static String getMacAddressString(ByteBuffer mac) {
- if (mac == null || mac.remaining() < ETHER_ADDR_LEN) return "invalid";
-
- byte[] bytes = new byte[ETHER_ADDR_LEN];
- mac.get(bytes, 0, bytes.length);
- Object[] printableBytes = new Object[bytes.length];
- int i = 0;
- for (byte b : bytes) printableBytes[i++] = new Byte(b);
-
- final String MAC48_FORMAT = "%02x:%02x:%02x:%02x:%02x:%02x";
- return String.format(MAC48_FORMAT, printableBytes);
- }
-
- /**
- * Convenience method to convert an int to a String.
- */
- public static String asString(int i) {
- return Integer.toString(i);
- }
-
- /**
- * Convenience method to read a byte as an unsigned int.
- */
- public static int asUint(byte b) {
- return (b & 0xff);
- }
-
- /**
- * Convenience method to read a short as an unsigned int.
- */
- public static int asUint(short s) {
- return (s & 0xffff);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/util/DataStallUtils.java b/packages/NetworkStack/src/android/net/util/DataStallUtils.java
deleted file mode 100644
index b6dbeb1..0000000
--- a/packages/NetworkStack/src/android/net/util/DataStallUtils.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-/**
- * Collection of utilities for data stall.
- */
-public class DataStallUtils {
- /**
- * Detect data stall via using dns timeout counts.
- */
- public static final int DATA_STALL_EVALUATION_TYPE_DNS = 1;
- // Default configuration values for data stall detection.
- public static final int DEFAULT_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD = 5;
- public static final int DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS = 60 * 1000;
- public static final int DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS = 30 * 60 * 1000;
- /**
- * The threshold value for the number of consecutive dns timeout events received to be a
- * signal of data stall. The number of consecutive timeouts needs to be {@code >=} this
- * threshold to be considered a data stall. Set the value to {@code <= 0} to disable. Note
- * that the value should be {@code > 0} if the DNS data stall detection is enabled.
- *
- */
- public static final String CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD =
- "data_stall_consecutive_dns_timeout_threshold";
-
- /**
- * The minimal time interval in milliseconds for data stall reevaluation.
- *
- */
- public static final String CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL =
- "data_stall_min_evaluate_interval";
-
- /**
- * DNS timeouts older than this timeout (in milliseconds) are not considered for detecting
- * a data stall.
- *
- */
- public static final String CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD =
- "data_stall_valid_dns_time_threshold";
-
- /**
- * Which data stall detection signal to use. This is a bitmask constructed by bitwise-or-ing
- * (i.e. {@code |}) the DATA_STALL_EVALUATION_TYPE_* values.
- *
- * Type: int
- * Valid values:
- * {@link #DATA_STALL_EVALUATION_TYPE_DNS} : Use dns as a signal.
- */
- public static final String CONFIG_DATA_STALL_EVALUATION_TYPE = "data_stall_evaluation_type";
- public static final int DEFAULT_DATA_STALL_EVALUATION_TYPES = DATA_STALL_EVALUATION_TYPE_DNS;
- // The default number of DNS events kept of the log kept for dns signal evaluation. Each event
- // is represented by a {@link com.android.server.connectivity.NetworkMonitor#DnsResult} objects.
- // It's also the size of array of {@link com.android.server.connectivity.nano.DnsEvent} kept in
- // metrics. Note that increasing the size may cause statsd log buffer bust. Need to check the
- // design in statsd when you try to increase the size.
- public static final int DEFAULT_DNS_LOG_SIZE = 20;
-}
diff --git a/packages/NetworkStack/src/android/net/util/FdEventsReader.java b/packages/NetworkStack/src/android/net/util/FdEventsReader.java
deleted file mode 100644
index 1380ea7..0000000
--- a/packages/NetworkStack/src/android/net/util/FdEventsReader.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
-import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.MessageQueue;
-import android.system.ErrnoException;
-import android.system.OsConstants;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-
-
-/**
- * This class encapsulates the mechanics of registering a file descriptor
- * with a thread's Looper and handling read events (and errors).
- *
- * Subclasses MUST implement createFd() and SHOULD override handlePacket(). They MAY override
- * onStop() and onStart().
- *
- * Subclasses can expect a call life-cycle like the following:
- *
- * [1] when a client calls start(), createFd() is called, followed by the onStart() hook if all
- * goes well. Implementations may override onStart() for additional initialization.
- *
- * [2] yield, waiting for read event or error notification:
- *
- * [a] readPacket() && handlePacket()
- *
- * [b] if (no error):
- * goto 2
- * else:
- * goto 3
- *
- * [3] when a client calls stop(), the onStop() hook is called (unless already stopped or never
- * started). Implementations may override onStop() for additional cleanup.
- *
- * The packet receive buffer is recycled on every read call, so subclasses
- * should make any copies they would like inside their handlePacket()
- * implementation.
- *
- * All public methods MUST only be called from the same thread with which
- * the Handler constructor argument is associated.
- *
- * @param <BufferType> the type of the buffer used to read data.
- * @hide
- */
-public abstract class FdEventsReader<BufferType> {
- private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
- private static final int UNREGISTER_THIS_FD = 0;
-
- @NonNull
- private final Handler mHandler;
- @NonNull
- private final MessageQueue mQueue;
- @NonNull
- private final BufferType mBuffer;
- @Nullable
- private FileDescriptor mFd;
- private long mPacketsReceived;
-
- protected static void closeFd(FileDescriptor fd) {
- try {
- SocketUtils.closeSocket(fd);
- } catch (IOException ignored) {
- }
- }
-
- protected FdEventsReader(@NonNull Handler h, @NonNull BufferType buffer) {
- mHandler = h;
- mQueue = mHandler.getLooper().getQueue();
- mBuffer = buffer;
- }
-
- /** Start this FdEventsReader. */
- public void start() {
- if (onCorrectThread()) {
- createAndRegisterFd();
- } else {
- mHandler.post(() -> {
- logError("start() called from off-thread", null);
- createAndRegisterFd();
- });
- }
- }
-
- /** Stop this FdEventsReader and destroy the file descriptor. */
- public void stop() {
- if (onCorrectThread()) {
- unregisterAndDestroyFd();
- } else {
- mHandler.post(() -> {
- logError("stop() called from off-thread", null);
- unregisterAndDestroyFd();
- });
- }
- }
-
- @NonNull
- public Handler getHandler() {
- return mHandler;
- }
-
- protected abstract int recvBufSize(@NonNull BufferType buffer);
-
- /** Returns the size of the receive buffer. */
- public int recvBufSize() {
- return recvBufSize(mBuffer);
- }
-
- /**
- * Get the number of successful calls to {@link #readPacket(FileDescriptor, Object)}.
- *
- * <p>A call was successful if {@link #readPacket(FileDescriptor, Object)} returned a value > 0.
- */
- public final long numPacketsReceived() {
- return mPacketsReceived;
- }
-
- /**
- * Subclasses MUST create the listening socket here, including setting all desired socket
- * options, interface or address/port binding, etc. The socket MUST be created nonblocking.
- */
- @Nullable
- protected abstract FileDescriptor createFd();
-
- /**
- * Implementations MUST return the bytes read or throw an Exception.
- *
- * <p>The caller may throw a {@link ErrnoException} with {@link OsConstants#EAGAIN} or
- * {@link OsConstants#EINTR}, in which case {@link FdEventsReader} will ignore the buffer
- * contents and respectively wait for further input or retry the read immediately. For all other
- * exceptions, the {@link FdEventsReader} will be stopped with no more interactions with this
- * method.
- */
- protected abstract int readPacket(@NonNull FileDescriptor fd, @NonNull BufferType buffer)
- throws Exception;
-
- /**
- * Called by the main loop for every packet. Any desired copies of
- * |recvbuf| should be made in here, as the underlying byte array is
- * reused across all reads.
- */
- protected void handlePacket(@NonNull BufferType recvbuf, int length) {}
-
- /**
- * Called by the main loop to log errors. In some cases |e| may be null.
- */
- protected void logError(@NonNull String msg, @Nullable Exception e) {}
-
- /**
- * Called by start(), if successful, just prior to returning.
- */
- protected void onStart() {}
-
- /**
- * Called by stop() just prior to returning.
- */
- protected void onStop() {}
-
- private void createAndRegisterFd() {
- if (mFd != null) return;
-
- try {
- mFd = createFd();
- } catch (Exception e) {
- logError("Failed to create socket: ", e);
- closeFd(mFd);
- mFd = null;
- }
-
- if (mFd == null) return;
-
- mQueue.addOnFileDescriptorEventListener(
- mFd,
- FD_EVENTS,
- (fd, events) -> {
- // Always call handleInput() so read/recvfrom are given
- // a proper chance to encounter a meaningful errno and
- // perhaps log a useful error message.
- if (!isRunning() || !handleInput()) {
- unregisterAndDestroyFd();
- return UNREGISTER_THIS_FD;
- }
- return FD_EVENTS;
- });
- onStart();
- }
-
- private boolean isRunning() {
- return (mFd != null) && mFd.valid();
- }
-
- // Keep trying to read until we get EAGAIN/EWOULDBLOCK or some fatal error.
- private boolean handleInput() {
- while (isRunning()) {
- final int bytesRead;
-
- try {
- bytesRead = readPacket(mFd, mBuffer);
- if (bytesRead < 1) {
- if (isRunning()) logError("Socket closed, exiting", null);
- break;
- }
- mPacketsReceived++;
- } catch (ErrnoException e) {
- if (e.errno == OsConstants.EAGAIN) {
- // We've read everything there is to read this time around.
- return true;
- } else if (e.errno == OsConstants.EINTR) {
- continue;
- } else {
- if (isRunning()) logError("readPacket error: ", e);
- break;
- }
- } catch (Exception e) {
- if (isRunning()) logError("readPacket error: ", e);
- break;
- }
-
- try {
- handlePacket(mBuffer, bytesRead);
- } catch (Exception e) {
- logError("handlePacket error: ", e);
- break;
- }
- }
-
- return false;
- }
-
- private void unregisterAndDestroyFd() {
- if (mFd == null) return;
-
- mQueue.removeOnFileDescriptorEventListener(mFd);
- closeFd(mFd);
- mFd = null;
- onStop();
- }
-
- private boolean onCorrectThread() {
- return (mHandler.getLooper() == Looper.myLooper());
- }
-}
diff --git a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
deleted file mode 100644
index 9bf1b96..0000000
--- a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.provider.DeviceConfig;
-import android.util.SparseArray;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.SocketException;
-import java.util.List;
-import java.util.function.Predicate;
-
-/**
- * Collection of utilities for the network stack.
- */
-public class NetworkStackUtils {
- // TODO: Refer to DeviceConfig definition.
- public static final String NAMESPACE_CONNECTIVITY = "connectivity";
-
- /**
- * A list of captive portal detection specifications used in addition to the fallback URLs.
- * Each spec has the format url@@/@@statusCodeRegex@@/@@contentRegex. Specs are separated
- * by "@@,@@".
- */
- public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS =
- "captive_portal_fallback_probe_specs";
-
- /**
- * A comma separated list of URLs used for captive portal detection in addition to the
- * fallback HTTP url associated with the CAPTIVE_PORTAL_FALLBACK_URL settings.
- */
- public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS =
- "captive_portal_other_fallback_urls";
-
- /**
- * Which User-Agent string to use in the header of the captive portal detection probes.
- * The User-Agent field is unset when this setting has no value (HttpUrlConnection default).
- */
- public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent";
-
- /**
- * Whether to use HTTPS for network validation. This is enabled by default and the setting
- * needs to be set to 0 to disable it. This setting is a misnomer because captive portals
- * don't actually use HTTPS, but it's consistent with the other settings.
- */
- public static final String CAPTIVE_PORTAL_USE_HTTPS = "captive_portal_use_https";
-
- /**
- * The URL used for HTTPS captive portal detection upon a new connection.
- * A 204 response code from the server is used for validation.
- */
- public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url";
-
- /**
- * The URL used for HTTP captive portal detection upon a new connection.
- * A 204 response code from the server is used for validation.
- */
- public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url";
-
- /**
- * The URL used for fallback HTTP captive portal detection when previous HTTP
- * and HTTPS captive portal detection attemps did not return a conclusive answer.
- */
- public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url";
-
- /**
- * What to do when connecting a network that presents a captive portal.
- * Must be one of the CAPTIVE_PORTAL_MODE_* constants above.
- *
- * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT.
- */
- public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode";
-
- /**
- * Don't attempt to detect captive portals.
- */
- public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0;
-
- /**
- * When detecting a captive portal, display a notification that
- * prompts the user to sign in.
- */
- public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1;
-
- /**
- * When detecting a captive portal, immediately disconnect from the
- * network and do not reconnect to that network in the future.
- */
- public static final int CAPTIVE_PORTAL_MODE_AVOID = 2;
-
- static {
- System.loadLibrary("networkstackutilsjni");
- }
-
- /**
- * @return True if the array is null or 0-length.
- */
- public static <T> boolean isEmpty(T[] array) {
- return array == null || array.length == 0;
- }
-
- /**
- * Close a socket, ignoring any exception while closing.
- */
- public static void closeSocketQuietly(FileDescriptor fd) {
- try {
- SocketUtils.closeSocket(fd);
- } catch (IOException ignored) {
- }
- }
-
- /**
- * Returns an int array from the given Integer list.
- */
- public static int[] convertToIntArray(@NonNull List<Integer> list) {
- int[] array = new int[list.size()];
- for (int i = 0; i < list.size(); i++) {
- array[i] = list.get(i);
- }
- return array;
- }
-
- /**
- * Returns a long array from the given long list.
- */
- public static long[] convertToLongArray(@NonNull List<Long> list) {
- long[] array = new long[list.size()];
- for (int i = 0; i < list.size(); i++) {
- array[i] = list.get(i);
- }
- return array;
- }
-
- /**
- * @return True if there exists at least one element in the sparse array for which
- * condition {@code predicate}
- */
- public static <T> boolean any(SparseArray<T> array, Predicate<T> predicate) {
- for (int i = 0; i < array.size(); ++i) {
- if (predicate.test(array.valueAt(i))) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Look up the value of a property for a particular namespace from {@link DeviceConfig}.
- * @param namespace The namespace containing the property to look up.
- * @param name The name of the property to look up.
- * @param defaultValue The value to return if the property does not exist or has no valid value.
- * @return the corresponding value, or defaultValue if none exists.
- */
- @Nullable
- public static String getDeviceConfigProperty(@NonNull String namespace, @NonNull String name,
- @Nullable String defaultValue) {
- String value = DeviceConfig.getProperty(namespace, name);
- return value != null ? value : defaultValue;
- }
-
- /**
- * Look up the value of a property for a particular namespace from {@link DeviceConfig}.
- * @param namespace The namespace containing the property to look up.
- * @param name The name of the property to look up.
- * @param defaultValue The value to return if the property does not exist or has no non-null
- * value.
- * @return the corresponding value, or defaultValue if none exists.
- */
- public static int getDeviceConfigPropertyInt(@NonNull String namespace, @NonNull String name,
- int defaultValue) {
- String value = getDeviceConfigProperty(namespace, name, null /* defaultValue */);
- try {
- return (value != null) ? Integer.parseInt(value) : defaultValue;
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- }
-
- /**
- * Attaches a socket filter that accepts DHCP packets to the given socket.
- */
- public static native void attachDhcpFilter(FileDescriptor fd) throws SocketException;
-
- /**
- * Attaches a socket filter that accepts ICMPv6 router advertisements to the given socket.
- * @param fd the socket's {@link FileDescriptor}.
- * @param packetType the hardware address type, one of ARPHRD_*.
- */
- public static native void attachRaFilter(FileDescriptor fd, int packetType)
- throws SocketException;
-
- /**
- * Attaches a socket filter that accepts L2-L4 signaling traffic required for IP connectivity.
- *
- * This includes: all ARP, ICMPv6 RS/RA/NS/NA messages, and DHCPv4 exchanges.
- *
- * @param fd the socket's {@link FileDescriptor}.
- * @param packetType the hardware address type, one of ARPHRD_*.
- */
- public static native void attachControlPacketFilter(FileDescriptor fd, int packetType)
- throws SocketException;
-
- /**
- * Add an entry into the ARP cache.
- */
- public static void addArpEntry(Inet4Address ipv4Addr, android.net.MacAddress ethAddr,
- String ifname, FileDescriptor fd) throws IOException {
- addArpEntry(ethAddr.toByteArray(), ipv4Addr.getAddress(), ifname, fd);
- }
-
- private static native void addArpEntry(byte[] ethAddr, byte[] netAddr, String ifname,
- FileDescriptor fd) throws IOException;
-
- /**
- * Return IP address and port in a string format.
- */
- public static String addressAndPortToString(InetAddress address, int port) {
- return String.format(
- (address instanceof Inet6Address) ? "[%s]:%d" : "%s:%d",
- address.getHostAddress(), port);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/util/PacketReader.java b/packages/NetworkStack/src/android/net/util/PacketReader.java
deleted file mode 100644
index 4aec6b6..0000000
--- a/packages/NetworkStack/src/android/net/util/PacketReader.java
+++ /dev/null
@@ -1,61 +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 android.net.util;
-
-import static java.lang.Math.max;
-
-import android.os.Handler;
-import android.system.Os;
-
-import java.io.FileDescriptor;
-
-/**
- * Specialization of {@link FdEventsReader} that reads packets into a byte array.
- *
- * TODO: rename this class to something more correctly descriptive (something
- * like [or less horrible than] FdReadEventsHandler?).
- *
- * @hide
- */
-public abstract class PacketReader extends FdEventsReader<byte[]> {
-
- public static final int DEFAULT_RECV_BUF_SIZE = 2 * 1024;
-
- protected PacketReader(Handler h) {
- this(h, DEFAULT_RECV_BUF_SIZE);
- }
-
- protected PacketReader(Handler h, int recvBufSize) {
- super(h, new byte[max(recvBufSize, DEFAULT_RECV_BUF_SIZE)]);
- }
-
- @Override
- protected final int recvBufSize(byte[] buffer) {
- return buffer.length;
- }
-
- /**
- * Subclasses MAY override this to change the default read() implementation
- * in favour of, say, recvfrom().
- *
- * Implementations MUST return the bytes read or throw an Exception.
- */
- @Override
- protected int readPacket(FileDescriptor fd, byte[] packetBuffer) throws Exception {
- return Os.read(fd, packetBuffer, 0, packetBuffer.length);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/util/SharedLog.java b/packages/NetworkStack/src/android/net/util/SharedLog.java
deleted file mode 100644
index 4fabf10..0000000
--- a/packages/NetworkStack/src/android/net/util/SharedLog.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.text.TextUtils;
-import android.util.LocalLog;
-import android.util.Log;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.StringJoiner;
-
-
-/**
- * Class to centralize logging functionality for tethering.
- *
- * All access to class methods other than dump() must be on the same thread.
- *
- * @hide
- */
-public class SharedLog {
- private static final int DEFAULT_MAX_RECORDS = 500;
- private static final String COMPONENT_DELIMITER = ".";
-
- private enum Category {
- NONE,
- ERROR,
- MARK,
- WARN,
- };
-
- private final LocalLog mLocalLog;
- // The tag to use for output to the system log. This is not output to the
- // LocalLog because that would be redundant.
- private final String mTag;
- // The component (or subcomponent) of a system that is sharing this log.
- // This can grow in depth if components call forSubComponent() to obtain
- // their SharedLog instance. The tag is not included in the component for
- // brevity.
- private final String mComponent;
-
- public SharedLog(String tag) {
- this(DEFAULT_MAX_RECORDS, tag);
- }
-
- public SharedLog(int maxRecords, String tag) {
- this(new LocalLog(maxRecords), tag, tag);
- }
-
- private SharedLog(LocalLog localLog, String tag, String component) {
- mLocalLog = localLog;
- mTag = tag;
- mComponent = component;
- }
-
- public String getTag() {
- return mTag;
- }
-
- /**
- * Create a SharedLog based on this log with an additional component prefix on each logged line.
- */
- public SharedLog forSubComponent(String component) {
- if (!isRootLogInstance()) {
- component = mComponent + COMPONENT_DELIMITER + component;
- }
- return new SharedLog(mLocalLog, mTag, component);
- }
-
- /**
- * Dump the contents of this log.
- *
- * <p>This method may be called on any thread.
- */
- public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- mLocalLog.readOnlyLocalLog().dump(fd, writer, args);
- }
-
- //////
- // Methods that both log an entry and emit it to the system log.
- //////
-
- /**
- * Log an error due to an exception. This does not include the exception stacktrace.
- *
- * <p>The log entry will be also added to the system log.
- * @see #e(String, Throwable)
- */
- public void e(Exception e) {
- Log.e(mTag, record(Category.ERROR, e.toString()));
- }
-
- /**
- * Log an error message.
- *
- * <p>The log entry will be also added to the system log.
- */
- public void e(String msg) {
- Log.e(mTag, record(Category.ERROR, msg));
- }
-
- /**
- * 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). The log entry will be also added to the system log.
- */
- 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);
- }
-
- /**
- * Log an informational message.
- *
- * <p>The log entry will be also added to the system log.
- */
- public void i(String msg) {
- Log.i(mTag, record(Category.NONE, msg));
- }
-
- /**
- * Log a warning message.
- *
- * <p>The log entry will be also added to the system log.
- */
- public void w(String msg) {
- Log.w(mTag, record(Category.WARN, msg));
- }
-
- //////
- // Methods that only log an entry (and do NOT emit to the system log).
- //////
-
- /**
- * Log a general message to be only included in the in-memory log.
- *
- * <p>The log entry will *not* be added to the system log.
- */
- public void log(String msg) {
- record(Category.NONE, msg);
- }
-
- /**
- * Log a general, formatted message to be only included in the in-memory log.
- *
- * <p>The log entry will *not* be added to the system log.
- * @see String#format(String, Object...)
- */
- public void logf(String fmt, Object... args) {
- log(String.format(fmt, args));
- }
-
- /**
- * Log a message with MARK level.
- *
- * <p>The log entry will *not* be added to the system log.
- */
- public void mark(String msg) {
- record(Category.MARK, msg);
- }
-
- private String record(Category category, String msg) {
- final String entry = logLine(category, msg);
- mLocalLog.log(entry);
- return entry;
- }
-
- private String logLine(Category category, String msg) {
- final StringJoiner sj = new StringJoiner(" ");
- if (!isRootLogInstance()) sj.add("[" + mComponent + "]");
- if (category != Category.NONE) sj.add(category.toString());
- return sj.add(msg).toString();
- }
-
- // Check whether this SharedLog instance is nominally the top level in
- // a potential hierarchy of shared logs (the root of a tree),
- // or is a subcomponent within the hierarchy.
- private boolean isRootLogInstance() {
- return TextUtils.isEmpty(mComponent) || mComponent.equals(mTag);
- }
-}
diff --git a/packages/NetworkStack/src/android/net/util/Stopwatch.java b/packages/NetworkStack/src/android/net/util/Stopwatch.java
deleted file mode 100644
index c316699..0000000
--- a/packages/NetworkStack/src/android/net/util/Stopwatch.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import android.os.SystemClock;
-
-
-/**
- * @hide
- */
-public class Stopwatch {
- private long mStartTimeMs;
- private long mStopTimeMs;
-
- public boolean isStarted() {
- return (mStartTimeMs > 0);
- }
-
- public boolean isStopped() {
- return (mStopTimeMs > 0);
- }
-
- public boolean isRunning() {
- return (isStarted() && !isStopped());
- }
-
- /**
- * Start the Stopwatch.
- */
- public Stopwatch start() {
- if (!isStarted()) {
- mStartTimeMs = SystemClock.elapsedRealtime();
- }
- return this;
- }
-
- /**
- * Stop the Stopwatch.
- * @return the total time recorded, in milliseconds, or 0 if not started.
- */
- public long stop() {
- if (isRunning()) {
- mStopTimeMs = SystemClock.elapsedRealtime();
- }
- // Return either the delta after having stopped, or 0.
- return (mStopTimeMs - mStartTimeMs);
- }
-
- /**
- * Return the total time recorded to date, in milliseconds.
- * If the Stopwatch is not running, returns the same value as stop(),
- * i.e. either the total time recorded before stopping or 0.
- */
- public long lap() {
- if (isRunning()) {
- return (SystemClock.elapsedRealtime() - mStartTimeMs);
- } else {
- return stop();
- }
- }
-
- /**
- * Reset the Stopwatch. It will be stopped when this method returns.
- */
- public void reset() {
- mStartTimeMs = 0;
- mStopTimeMs = 0;
- }
-}
diff --git a/packages/NetworkStack/src/com/android/networkstack/metrics/DataStallDetectionStats.java b/packages/NetworkStack/src/com/android/networkstack/metrics/DataStallDetectionStats.java
deleted file mode 100644
index 2523ecd..0000000
--- a/packages/NetworkStack/src/com/android/networkstack/metrics/DataStallDetectionStats.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2019 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.networkstack.metrics;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.util.NetworkStackUtils;
-import android.net.wifi.WifiInfo;
-
-import com.android.internal.util.HexDump;
-import com.android.server.connectivity.nano.CellularData;
-import com.android.server.connectivity.nano.DataStallEventProto;
-import com.android.server.connectivity.nano.DnsEvent;
-import com.android.server.connectivity.nano.WifiData;
-
-import com.google.protobuf.nano.MessageNano;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Class to record the stats of detection level information for data stall.
- *
- * @hide
- */
-public final class DataStallDetectionStats {
- private static final int UNKNOWN_SIGNAL_STRENGTH = -1;
- @NonNull
- final byte[] mCellularInfo;
- @NonNull
- final byte[] mWifiInfo;
- @NonNull
- final byte[] mDns;
- final int mEvaluationType;
- final int mNetworkType;
-
- public DataStallDetectionStats(@Nullable byte[] cell, @Nullable byte[] wifi,
- @NonNull int[] returnCode, @NonNull long[] dnsTime, int evalType, int netType) {
- mCellularInfo = emptyCellDataIfNull(cell);
- mWifiInfo = emptyWifiInfoIfNull(wifi);
-
- DnsEvent dns = new DnsEvent();
- dns.dnsReturnCode = returnCode;
- dns.dnsTime = dnsTime;
- mDns = MessageNano.toByteArray(dns);
- mEvaluationType = evalType;
- mNetworkType = netType;
- }
-
- private byte[] emptyCellDataIfNull(@Nullable byte[] cell) {
- if (cell != null) return cell;
-
- CellularData data = new CellularData();
- data.ratType = DataStallEventProto.RADIO_TECHNOLOGY_UNKNOWN;
- data.networkMccmnc = "";
- data.simMccmnc = "";
- data.signalStrength = UNKNOWN_SIGNAL_STRENGTH;
- return MessageNano.toByteArray(data);
- }
-
- private byte[] emptyWifiInfoIfNull(@Nullable byte[] wifi) {
- if (wifi != null) return wifi;
-
- WifiData data = new WifiData();
- data.wifiBand = DataStallEventProto.AP_BAND_UNKNOWN;
- data.signalStrength = UNKNOWN_SIGNAL_STRENGTH;
- return MessageNano.toByteArray(data);
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append("type: ").append(mNetworkType)
- .append(", evaluation type: ")
- .append(mEvaluationType)
- .append(", wifi info: ")
- .append(HexDump.toHexString(mWifiInfo))
- .append(", cell info: ")
- .append(HexDump.toHexString(mCellularInfo))
- .append(", dns: ")
- .append(HexDump.toHexString(mDns));
- return sb.toString();
- }
-
- @Override
- public boolean equals(@Nullable final Object o) {
- if (!(o instanceof DataStallDetectionStats)) return false;
- final DataStallDetectionStats other = (DataStallDetectionStats) o;
- return (mNetworkType == other.mNetworkType)
- && (mEvaluationType == other.mEvaluationType)
- && Arrays.equals(mWifiInfo, other.mWifiInfo)
- && Arrays.equals(mCellularInfo, other.mCellularInfo)
- && Arrays.equals(mDns, other.mDns);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mNetworkType, mEvaluationType, mWifiInfo, mCellularInfo, mDns);
- }
-
- /**
- * Utility to create an instance of {@Link DataStallDetectionStats}
- *
- * @hide
- */
- public static class Builder {
- @Nullable
- private byte[] mCellularInfo;
- @Nullable
- private byte[] mWifiInfo;
- @NonNull
- private final List<Integer> mDnsReturnCode = new ArrayList<Integer>();
- @NonNull
- private final List<Long> mDnsTimeStamp = new ArrayList<Long>();
- private int mEvaluationType;
- private int mNetworkType;
-
- /**
- * Add a dns event into Builder.
- *
- * @param code the return code of the dns event.
- * @param timeMs the elapsedRealtime in ms that the the dns event was received from netd.
- * @return {@code this} {@link Builder} instance.
- */
- public Builder addDnsEvent(int code, long timeMs) {
- mDnsReturnCode.add(code);
- mDnsTimeStamp.add(timeMs);
- return this;
- }
-
- /**
- * Set the dns evaluation type into Builder.
- *
- * @param type the return code of the dns event.
- * @return {@code this} {@link Builder} instance.
- */
- public Builder setEvaluationType(int type) {
- mEvaluationType = type;
- return this;
- }
-
- /**
- * Set the network type into Builder.
- *
- * @param type the network type of the logged network.
- * @return {@code this} {@link Builder} instance.
- */
- public Builder setNetworkType(int type) {
- mNetworkType = type;
- return this;
- }
-
- /**
- * Set the wifi data into Builder.
- *
- * @param info a {@link WifiInfo} of the connected wifi network.
- * @return {@code this} {@link Builder} instance.
- */
- public Builder setWiFiData(@Nullable final WifiInfo info) {
- WifiData data = new WifiData();
- data.wifiBand = getWifiBand(info);
- data.signalStrength = (info != null) ? info.getRssi() : UNKNOWN_SIGNAL_STRENGTH;
- mWifiInfo = MessageNano.toByteArray(data);
- return this;
- }
-
- private static int getWifiBand(@Nullable final WifiInfo info) {
- if (info == null) return DataStallEventProto.AP_BAND_UNKNOWN;
-
- int freq = info.getFrequency();
- // Refer to ScanResult.is5GHz() and ScanResult.is24GHz().
- if (freq > 4900 && freq < 5900) {
- return DataStallEventProto.AP_BAND_5GHZ;
- } else if (freq > 2400 && freq < 2500) {
- return DataStallEventProto.AP_BAND_2GHZ;
- } else {
- return DataStallEventProto.AP_BAND_UNKNOWN;
- }
- }
-
- /**
- * Set the cellular data into Builder.
- *
- * @param radioType the radio technology of the logged cellular network.
- * @param roaming a boolean indicates if logged cellular network is roaming or not.
- * @param networkMccmnc the mccmnc of the camped network.
- * @param simMccmnc the mccmnc of the sim.
- * @return {@code this} {@link Builder} instance.
- */
- public Builder setCellData(int radioType, boolean roaming,
- @NonNull String networkMccmnc, @NonNull String simMccmnc, int ss) {
- CellularData data = new CellularData();
- data.ratType = radioType;
- data.isRoaming = roaming;
- data.networkMccmnc = networkMccmnc;
- data.simMccmnc = simMccmnc;
- data.signalStrength = ss;
- mCellularInfo = MessageNano.toByteArray(data);
- return this;
- }
-
- /**
- * Create a new {@Link DataStallDetectionStats}.
- */
- public DataStallDetectionStats build() {
- return new DataStallDetectionStats(mCellularInfo, mWifiInfo,
- NetworkStackUtils.convertToIntArray(mDnsReturnCode),
- NetworkStackUtils.convertToLongArray(mDnsTimeStamp),
- mEvaluationType, mNetworkType);
- }
- }
-}
diff --git a/packages/NetworkStack/src/com/android/networkstack/metrics/DataStallStatsUtils.java b/packages/NetworkStack/src/com/android/networkstack/metrics/DataStallStatsUtils.java
deleted file mode 100644
index 9308901..0000000
--- a/packages/NetworkStack/src/com/android/networkstack/metrics/DataStallStatsUtils.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2019 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.networkstack.metrics;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.captiveportal.CaptivePortalProbeResult;
-import android.util.Log;
-
-import com.android.internal.util.HexDump;
-import com.android.server.connectivity.nano.DataStallEventProto;
-
-/**
- * Collection of utilities for data stall metrics.
- *
- * To see if the logs are properly sent to statsd, execute following command.
- *
- * $ adb shell cmd stats print-logs
- * $ adb logcat | grep statsd OR $ adb logcat -b stats
- *
- * @hide
- */
-public class DataStallStatsUtils {
- private static final String TAG = DataStallStatsUtils.class.getSimpleName();
- private static final boolean DBG = false;
-
- private static int probeResultToEnum(@Nullable final CaptivePortalProbeResult result) {
- if (result == null) return DataStallEventProto.INVALID;
-
- if (result.isSuccessful()) {
- return DataStallEventProto.VALID;
- } else if (result.isPortal()) {
- return DataStallEventProto.PORTAL;
- } else if (result.isPartialConnectivity()) {
- return DataStallEventProto.PARTIAL;
- } else {
- return DataStallEventProto.INVALID;
- }
- }
-
- /**
- * Write the metric to {@link StatsLog}.
- */
- public static void write(@NonNull final DataStallDetectionStats stats,
- @NonNull final CaptivePortalProbeResult result) {
- int validationResult = probeResultToEnum(result);
- if (DBG) {
- Log.d(TAG, "write: " + stats + " with result: " + validationResult
- + ", dns: " + HexDump.toHexString(stats.mDns));
- }
- NetworkStackStatsLog.write(NetworkStackStatsLog.DATA_STALL_EVENT,
- stats.mEvaluationType,
- validationResult,
- stats.mNetworkType,
- stats.mWifiInfo,
- stats.mCellularInfo,
- stats.mDns);
- }
-}
diff --git a/packages/NetworkStack/src/com/android/networkstack/util/DnsUtils.java b/packages/NetworkStack/src/com/android/networkstack/util/DnsUtils.java
deleted file mode 100644
index 4767d55..0000000
--- a/packages/NetworkStack/src/com/android/networkstack/util/DnsUtils.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2019 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.networkstack.util;
-
-import static android.net.DnsResolver.FLAG_NO_CACHE_LOOKUP;
-import static android.net.DnsResolver.TYPE_A;
-import static android.net.DnsResolver.TYPE_AAAA;
-
-import android.annotation.NonNull;
-import android.net.DnsResolver;
-import android.net.Network;
-import android.net.TrafficStats;
-import android.util.Log;
-
-import com.android.internal.util.TrafficStatsConstants;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * Collection of utilities for dns query.
- */
-public class DnsUtils {
- // Decide what queries to make depending on what IP addresses are on the system.
- public static final int TYPE_ADDRCONFIG = -1;
- private static final String TAG = DnsUtils.class.getSimpleName();
-
- /**
- * Return both A and AAAA query results regardless the ip address type of the giving network.
- * Used for probing in NetworkMonitor.
- */
- @NonNull
- public static InetAddress[] getAllByName(@NonNull final DnsResolver dnsResolver,
- @NonNull final Network network, @NonNull String host, int timeout)
- throws UnknownHostException {
- final List<InetAddress> result = new ArrayList<InetAddress>();
-
- try {
- result.addAll(Arrays.asList(
- getAllByName(dnsResolver, network, host, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
- timeout)));
- } catch (UnknownHostException e) {
- // Might happen if the host is v4-only, still need to query TYPE_A
- }
- try {
- result.addAll(Arrays.asList(
- getAllByName(dnsResolver, network, host, TYPE_A, FLAG_NO_CACHE_LOOKUP,
- timeout)));
- } catch (UnknownHostException e) {
- // Might happen if the host is v6-only, still need to return AAAA answers
- }
- if (result.size() == 0) {
- throw new UnknownHostException(host);
- }
- return result.toArray(new InetAddress[0]);
- }
-
- /**
- * Return dns query result based on the given QueryType(TYPE_A, TYPE_AAAA) or TYPE_ADDRCONFIG.
- * Used for probing in NetworkMonitor.
- */
- @NonNull
- public static InetAddress[] getAllByName(@NonNull final DnsResolver dnsResolver,
- @NonNull final Network network, @NonNull final String host, int type, int flag,
- int timeoutMs) throws UnknownHostException {
- final CountDownLatch latch = new CountDownLatch(1);
- final AtomicReference<List<InetAddress>> resultRef = new AtomicReference<>();
-
- final DnsResolver.Callback<List<InetAddress>> callback =
- new DnsResolver.Callback<List<InetAddress>>() {
- @Override
- public void onAnswer(List<InetAddress> answer, int rcode) {
- if (rcode == 0) {
- resultRef.set(answer);
- }
- latch.countDown();
- }
-
- @Override
- public void onError(@NonNull DnsResolver.DnsException e) {
- Log.d(TAG, "DNS error resolving " + host + ": " + e.getMessage());
- latch.countDown();
- }
- };
- final int oldTag = TrafficStats.getAndSetThreadStatsTag(
- TrafficStatsConstants.TAG_SYSTEM_PROBE);
-
- if (type == TYPE_ADDRCONFIG) {
- dnsResolver.query(network, host, flag, r -> r.run(), null /* cancellationSignal */,
- callback);
- } else {
- dnsResolver.query(network, host, type, flag, r -> r.run(),
- null /* cancellationSignal */, callback);
- }
-
- TrafficStats.setThreadStatsTag(oldTag);
-
- try {
- latch.await(timeoutMs, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- }
-
- final List<InetAddress> result = resultRef.get();
- if (result == null || result.size() == 0) {
- throw new UnknownHostException(host);
- }
-
- return result.toArray(new InetAddress[0]);
- }
-}
diff --git a/packages/NetworkStack/src/com/android/server/NetworkObserver.java b/packages/NetworkStack/src/com/android/server/NetworkObserver.java
deleted file mode 100644
index cccec0b..0000000
--- a/packages/NetworkStack/src/com/android/server/NetworkObserver.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.net.LinkAddress;
-import android.net.RouteInfo;
-
-/**
- * Observer for network events, to use with {@link NetworkObserverRegistry}.
- */
-public interface NetworkObserver {
-
- /**
- * @see android.net.INetdUnsolicitedEventListener#onInterfaceChanged(java.lang.String, boolean)
- */
- default void onInterfaceChanged(String ifName, boolean up) {}
-
- /**
- * @see android.net.INetdUnsolicitedEventListener#onInterfaceRemoved(String)
- */
- default void onInterfaceRemoved(String ifName) {}
-
- /**
- * @see android.net.INetdUnsolicitedEventListener
- * #onInterfaceAddressUpdated(String, String, int, int)
- */
- default void onInterfaceAddressUpdated(LinkAddress address, String ifName) {}
-
- /**
- * @see android.net.INetdUnsolicitedEventListener
- * #onInterfaceAddressRemoved(String, String, int, int)
- */
- default void onInterfaceAddressRemoved(LinkAddress address, String ifName) {}
-
- /**
- * @see android.net.INetdUnsolicitedEventListener#onInterfaceLinkStateChanged(String, boolean)
- */
- default void onInterfaceLinkStateChanged(String ifName, boolean up) {}
-
- /**
- * @see android.net.INetdUnsolicitedEventListener#onInterfaceAdded(String)
- */
- default void onInterfaceAdded(String ifName) {}
-
- /**
- * @see android.net.INetdUnsolicitedEventListener
- * #onInterfaceClassActivityChanged(boolean, int, long, int)
- */
- default void onInterfaceClassActivityChanged(
- boolean isActive, int label, long timestamp, int uid) {}
-
- /**
- * @see android.net.INetdUnsolicitedEventListener#onQuotaLimitReached(String, String)
- */
- default void onQuotaLimitReached(String alertName, String ifName) {}
-
- /**
- * @see android.net.INetdUnsolicitedEventListener
- * #onInterfaceDnsServerInfo(String, long, String[])
- */
- default void onInterfaceDnsServerInfo(String ifName, long lifetime, String[] servers) {}
-
- /**
- * @see android.net.INetdUnsolicitedEventListener
- * #onRouteChanged(boolean, String, String, String)
- */
- default void onRouteUpdated(RouteInfo route) {}
-
- /**
- * @see android.net.INetdUnsolicitedEventListener
- * #onRouteChanged(boolean, String, String, String)
- */
- default void onRouteRemoved(RouteInfo route) {}
-}
diff --git a/packages/NetworkStack/src/com/android/server/NetworkObserverRegistry.java b/packages/NetworkStack/src/com/android/server/NetworkObserverRegistry.java
deleted file mode 100644
index afe166b..0000000
--- a/packages/NetworkStack/src/com/android/server/NetworkObserverRegistry.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server;
-
-import static android.net.RouteInfo.RTN_UNICAST;
-
-import android.annotation.NonNull;
-import android.net.INetd;
-import android.net.INetdUnsolicitedEventListener;
-import android.net.InetAddresses;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.RouteInfo;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * A class for reporting network events to clients.
- *
- * Implements INetdUnsolicitedEventListener and registers with netd, and relays those events to
- * all INetworkManagementEventObserver objects that have registered with it.
- */
-public class NetworkObserverRegistry extends INetdUnsolicitedEventListener.Stub {
- private static final String TAG = NetworkObserverRegistry.class.getSimpleName();
-
- /**
- * Constructs a new NetworkObserverRegistry.
- *
- * <p>Only one registry should be used per process since netd will silently ignore multiple
- * registrations from the same process.
- */
- NetworkObserverRegistry() {}
-
- /**
- * Start listening for Netd events.
- *
- * <p>This should be called before allowing any observer to be registered.
- */
- void register(@NonNull INetd netd) throws RemoteException {
- netd.registerUnsolicitedEventListener(this);
- }
-
- private final ConcurrentHashMap<NetworkObserver, Optional<Handler>> mObservers =
- new ConcurrentHashMap<>();
-
- /**
- * Registers the specified observer and start sending callbacks to it.
- * This method may be called on any thread.
- */
- public void registerObserver(@NonNull NetworkObserver observer, @NonNull Handler handler) {
- if (handler == null) {
- throw new IllegalArgumentException("handler must be non-null");
- }
- mObservers.put(observer, Optional.of(handler));
- }
-
- /**
- * Registers the specified observer, and start sending callbacks to it.
- *
- * <p>This method must only be called with callbacks that are nonblocking, such as callbacks
- * that only send a message to a StateMachine.
- */
- public void registerObserverForNonblockingCallback(@NonNull NetworkObserver observer) {
- mObservers.put(observer, Optional.empty());
- }
-
- /**
- * Unregisters the specified observer and stop sending callbacks to it.
- * This method may be called on any thread.
- */
- public void unregisterObserver(@NonNull NetworkObserver observer) {
- mObservers.remove(observer);
- }
-
- @FunctionalInterface
- private interface NetworkObserverEventCallback {
- void sendCallback(NetworkObserver o);
- }
-
- private void invokeForAllObservers(@NonNull final NetworkObserverEventCallback callback) {
- // ConcurrentHashMap#entrySet is weakly consistent: observers that were in the map before
- // creation will be processed, those added during traversal may or may not.
- for (Map.Entry<NetworkObserver, Optional<Handler>> entry : mObservers.entrySet()) {
- final NetworkObserver observer = entry.getKey();
- final Optional<Handler> handler = entry.getValue();
- if (handler.isPresent()) {
- handler.get().post(() -> callback.sendCallback(observer));
- return;
- }
-
- try {
- callback.sendCallback(observer);
- } catch (RuntimeException e) {
- Log.e(TAG, "Error sending callback to observer", e);
- }
- }
- }
-
- @Override
- public void onInterfaceClassActivityChanged(boolean isActive,
- int label, long timestamp, int uid) {
- invokeForAllObservers(o -> o.onInterfaceClassActivityChanged(
- isActive, label, timestamp, uid));
- }
-
- /**
- * Notify our observers of a limit reached.
- */
- @Override
- public void onQuotaLimitReached(String alertName, String ifName) {
- invokeForAllObservers(o -> o.onQuotaLimitReached(alertName, ifName));
- }
-
- @Override
- public void onInterfaceDnsServerInfo(String ifName, long lifetime, String[] servers) {
- invokeForAllObservers(o -> o.onInterfaceDnsServerInfo(ifName, lifetime, servers));
- }
-
- @Override
- public void onInterfaceAddressUpdated(String addr, String ifName, int flags, int scope) {
- final LinkAddress address = new LinkAddress(addr, flags, scope);
- invokeForAllObservers(o -> o.onInterfaceAddressUpdated(address, ifName));
- }
-
- @Override
- public void onInterfaceAddressRemoved(String addr,
- String ifName, int flags, int scope) {
- final LinkAddress address = new LinkAddress(addr, flags, scope);
- invokeForAllObservers(o -> o.onInterfaceAddressRemoved(address, ifName));
- }
-
- @Override
- public void onInterfaceAdded(String ifName) {
- invokeForAllObservers(o -> o.onInterfaceAdded(ifName));
- }
-
- @Override
- public void onInterfaceRemoved(String ifName) {
- invokeForAllObservers(o -> o.onInterfaceRemoved(ifName));
- }
-
- @Override
- public void onInterfaceChanged(String ifName, boolean up) {
- invokeForAllObservers(o -> o.onInterfaceChanged(ifName, up));
- }
-
- @Override
- public void onInterfaceLinkStateChanged(String ifName, boolean up) {
- invokeForAllObservers(o -> o.onInterfaceLinkStateChanged(ifName, up));
- }
-
- @Override
- public void onRouteChanged(boolean updated, String route, String gateway, String ifName) {
- final RouteInfo processRoute = new RouteInfo(new IpPrefix(route),
- ("".equals(gateway)) ? null : InetAddresses.parseNumericAddress(gateway),
- ifName, RTN_UNICAST);
- if (updated) {
- invokeForAllObservers(o -> o.onRouteUpdated(processRoute));
- } else {
- invokeForAllObservers(o -> o.onRouteRemoved(processRoute));
- }
- }
-
- @Override
- public void onStrictCleartextDetected(int uid, String hex) {}
-
- @Override
- public int getInterfaceVersion() {
- return INetdUnsolicitedEventListener.VERSION;
- }
-}
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
deleted file mode 100644
index 9bf4457..0000000
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ /dev/null
@@ -1,389 +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.server;
-
-import static android.net.dhcp.IDhcpServer.STATUS_INVALID_ARGUMENT;
-import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
-import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR;
-
-import static com.android.server.util.PermissionUtil.checkDumpPermission;
-import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.net.ConnectivityManager;
-import android.net.IIpMemoryStore;
-import android.net.IIpMemoryStoreCallbacks;
-import android.net.INetd;
-import android.net.INetworkMonitor;
-import android.net.INetworkMonitorCallbacks;
-import android.net.INetworkStackConnector;
-import android.net.LinkProperties;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.PrivateDnsConfigParcel;
-import android.net.dhcp.DhcpServer;
-import android.net.dhcp.DhcpServingParams;
-import android.net.dhcp.DhcpServingParamsParcel;
-import android.net.dhcp.IDhcpServerCallbacks;
-import android.net.ip.IIpClientCallbacks;
-import android.net.ip.IpClient;
-import android.net.shared.PrivateDnsConfig;
-import android.net.util.SharedLog;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.ArraySet;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.connectivity.NetworkMonitor;
-import com.android.server.connectivity.ipmemorystore.IpMemoryStoreService;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-
-/**
- * Android service used to start the network stack when bound to via an intent.
- *
- * <p>The service returns a binder for the system server to communicate with the network stack.
- */
-public class NetworkStackService extends Service {
- private static final String TAG = NetworkStackService.class.getSimpleName();
- private static NetworkStackConnector sConnector;
-
- /**
- * Create a binder connector for the system server to communicate with the network stack.
- *
- * <p>On platforms where the network stack runs in the system server process, this method may
- * be called directly instead of obtaining the connector by binding to the service.
- */
- public static synchronized IBinder makeConnector(Context context) {
- if (sConnector == null) {
- sConnector = new NetworkStackConnector(context);
- }
- return sConnector;
- }
-
- @NonNull
- @Override
- public IBinder onBind(Intent intent) {
- return makeConnector(this);
- }
-
- /**
- * An interface for internal clients of the network stack service that can return
- * or create inline instances of the service it manages.
- */
- public interface NetworkStackServiceManager {
- /**
- * Get an instance of the IpMemoryStoreService.
- */
- IIpMemoryStore getIpMemoryStoreService();
- }
-
- private static class NetworkStackConnector extends INetworkStackConnector.Stub
- implements NetworkStackServiceManager {
- private static final int NUM_VALIDATION_LOG_LINES = 20;
- private final Context mContext;
- private final INetd mNetd;
- private final NetworkObserverRegistry mObserverRegistry;
- private final ConnectivityManager mCm;
- @GuardedBy("mIpClients")
- private final ArrayList<WeakReference<IpClient>> mIpClients = new ArrayList<>();
- private final IpMemoryStoreService mIpMemoryStoreService;
-
- private static final int MAX_VALIDATION_LOGS = 10;
- @GuardedBy("mValidationLogs")
- private final ArrayDeque<SharedLog> mValidationLogs = new ArrayDeque<>(MAX_VALIDATION_LOGS);
-
- private static final String DUMPSYS_ARG_VERSION = "version";
-
- /** Version of the framework AIDL interfaces observed. Should hold only one value. */
- @GuardedBy("mFrameworkAidlVersions")
- private final ArraySet<Integer> mFrameworkAidlVersions = new ArraySet<>(1);
- private final int mNetdAidlVersion;
-
- private SharedLog addValidationLogs(Network network, String name) {
- final SharedLog log = new SharedLog(NUM_VALIDATION_LOG_LINES, network + " - " + name);
- synchronized (mValidationLogs) {
- while (mValidationLogs.size() >= MAX_VALIDATION_LOGS) {
- mValidationLogs.removeLast();
- }
- mValidationLogs.addFirst(log);
- }
- return log;
- }
-
- NetworkStackConnector(Context context) {
- mContext = context;
- mNetd = INetd.Stub.asInterface(
- (IBinder) context.getSystemService(Context.NETD_SERVICE));
- mObserverRegistry = new NetworkObserverRegistry();
- mCm = context.getSystemService(ConnectivityManager.class);
- mIpMemoryStoreService = new IpMemoryStoreService(context);
-
- int netdVersion;
- try {
- netdVersion = mNetd.getInterfaceVersion();
- } catch (RemoteException e) {
- mLog.e("Error obtaining INetd version", e);
- netdVersion = -1;
- }
- mNetdAidlVersion = netdVersion;
-
- try {
- mObserverRegistry.register(mNetd);
- } catch (RemoteException e) {
- mLog.e("Error registering observer on Netd", e);
- }
- }
-
- private void updateSystemAidlVersion(final int version) {
- synchronized (mFrameworkAidlVersions) {
- mFrameworkAidlVersions.add(version);
- }
- }
-
- @NonNull
- private final SharedLog mLog = new SharedLog(TAG);
-
- @Override
- public void makeDhcpServer(@NonNull String ifName, @NonNull DhcpServingParamsParcel params,
- @NonNull IDhcpServerCallbacks cb) throws RemoteException {
- checkNetworkStackCallingPermission();
- updateSystemAidlVersion(cb.getInterfaceVersion());
- final DhcpServer server;
- try {
- server = new DhcpServer(
- ifName,
- DhcpServingParams.fromParcelableObject(params),
- mLog.forSubComponent(ifName + ".DHCP"));
- } catch (DhcpServingParams.InvalidParameterException e) {
- mLog.e("Invalid DhcpServingParams", e);
- cb.onDhcpServerCreated(STATUS_INVALID_ARGUMENT, null);
- return;
- } catch (Exception e) {
- mLog.e("Unknown error starting DhcpServer", e);
- cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null);
- return;
- }
- cb.onDhcpServerCreated(STATUS_SUCCESS, server);
- }
-
- @Override
- public void makeNetworkMonitor(Network network, String name, INetworkMonitorCallbacks cb)
- throws RemoteException {
- checkNetworkStackCallingPermission();
- updateSystemAidlVersion(cb.getInterfaceVersion());
- final SharedLog log = addValidationLogs(network, name);
- final NetworkMonitor nm = new NetworkMonitor(mContext, cb, network, log);
- cb.onNetworkMonitorCreated(new NetworkMonitorImpl(nm));
- }
-
- @Override
- public void makeIpClient(String ifName, IIpClientCallbacks cb) throws RemoteException {
- checkNetworkStackCallingPermission();
- updateSystemAidlVersion(cb.getInterfaceVersion());
- final IpClient ipClient = new IpClient(mContext, ifName, cb, mObserverRegistry, this);
-
- synchronized (mIpClients) {
- final Iterator<WeakReference<IpClient>> it = mIpClients.iterator();
- while (it.hasNext()) {
- final IpClient ipc = it.next().get();
- if (ipc == null) {
- it.remove();
- }
- }
- mIpClients.add(new WeakReference<>(ipClient));
- }
-
- cb.onIpClientCreated(ipClient.makeConnector());
- }
-
- @Override
- public IIpMemoryStore getIpMemoryStoreService() {
- return mIpMemoryStoreService;
- }
-
- @Override
- public void fetchIpMemoryStore(@NonNull final IIpMemoryStoreCallbacks cb)
- throws RemoteException {
- checkNetworkStackCallingPermission();
- updateSystemAidlVersion(cb.getInterfaceVersion());
- cb.onIpMemoryStoreFetched(mIpMemoryStoreService);
- }
-
- @Override
- protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
- @Nullable String[] args) {
- checkDumpPermission();
-
- final IndentingPrintWriter pw = new IndentingPrintWriter(fout, " ");
- pw.println("NetworkStack version:");
- dumpVersion(pw);
- pw.println();
-
- if (args != null && args.length >= 1 && DUMPSYS_ARG_VERSION.equals(args[0])) {
- return;
- }
-
- pw.println("NetworkStack logs:");
- mLog.dump(fd, pw, args);
-
- // Dump full IpClient logs for non-GCed clients
- pw.println();
- pw.println("Recently active IpClient logs:");
- final ArrayList<IpClient> ipClients = new ArrayList<>();
- final HashSet<String> dumpedIpClientIfaces = new HashSet<>();
- synchronized (mIpClients) {
- for (WeakReference<IpClient> ipcRef : mIpClients) {
- final IpClient ipc = ipcRef.get();
- if (ipc != null) {
- ipClients.add(ipc);
- }
- }
- }
-
- for (IpClient ipc : ipClients) {
- pw.println(ipc.getName());
- pw.increaseIndent();
- ipc.dump(fd, pw, args);
- pw.decreaseIndent();
- dumpedIpClientIfaces.add(ipc.getInterfaceName());
- }
-
- // State machine and connectivity metrics logs are kept for GCed IpClients
- pw.println();
- pw.println("Other IpClient logs:");
- IpClient.dumpAllLogs(fout, dumpedIpClientIfaces);
-
- pw.println();
- pw.println("Validation logs (most recent first):");
- synchronized (mValidationLogs) {
- for (SharedLog p : mValidationLogs) {
- pw.println(p.getTag());
- pw.increaseIndent();
- p.dump(fd, pw, args);
- pw.decreaseIndent();
- }
- }
- }
-
- /**
- * Dump version information of the module and detected system version.
- */
- private void dumpVersion(@NonNull PrintWriter fout) {
- fout.println("NetworkStackConnector: " + this.VERSION);
- synchronized (mFrameworkAidlVersions) {
- fout.println("SystemServer: " + mFrameworkAidlVersions);
- }
- fout.println("Netd: " + mNetdAidlVersion);
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
- }
-
- private static class NetworkMonitorImpl extends INetworkMonitor.Stub {
- private final NetworkMonitor mNm;
-
- NetworkMonitorImpl(NetworkMonitor nm) {
- mNm = nm;
- }
-
- @Override
- public void start() {
- checkNetworkStackCallingPermission();
- mNm.start();
- }
-
- @Override
- public void launchCaptivePortalApp() {
- checkNetworkStackCallingPermission();
- mNm.launchCaptivePortalApp();
- }
-
- @Override
- public void notifyCaptivePortalAppFinished(int response) {
- checkNetworkStackCallingPermission();
- mNm.notifyCaptivePortalAppFinished(response);
- }
-
- @Override
- public void setAcceptPartialConnectivity() {
- checkNetworkStackCallingPermission();
- mNm.setAcceptPartialConnectivity();
- }
-
- @Override
- public void forceReevaluation(int uid) {
- checkNetworkStackCallingPermission();
- mNm.forceReevaluation(uid);
- }
-
- @Override
- public void notifyPrivateDnsChanged(PrivateDnsConfigParcel config) {
- checkNetworkStackCallingPermission();
- mNm.notifyPrivateDnsSettingsChanged(PrivateDnsConfig.fromParcel(config));
- }
-
- @Override
- public void notifyDnsResponse(int returnCode) {
- checkNetworkStackCallingPermission();
- mNm.notifyDnsResponse(returnCode);
- }
-
- @Override
- public void notifyNetworkConnected(LinkProperties lp, NetworkCapabilities nc) {
- checkNetworkStackCallingPermission();
- mNm.notifyNetworkConnected(lp, nc);
- }
-
- @Override
- public void notifyNetworkDisconnected() {
- checkNetworkStackCallingPermission();
- mNm.notifyNetworkDisconnected();
- }
-
- @Override
- public void notifyLinkPropertiesChanged(LinkProperties lp) {
- checkNetworkStackCallingPermission();
- mNm.notifyLinkPropertiesChanged(lp);
- }
-
- @Override
- public void notifyNetworkCapabilitiesChanged(NetworkCapabilities nc) {
- checkNetworkStackCallingPermission();
- mNm.notifyNetworkCapabilitiesChanged(nc);
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
- }
-}
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
deleted file mode 100644
index 4e40ba4..0000000
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ /dev/null
@@ -1,2143 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.connectivity;
-
-import static android.net.CaptivePortal.APP_RETURN_DISMISSED;
-import static android.net.CaptivePortal.APP_RETURN_UNWANTED;
-import static android.net.CaptivePortal.APP_RETURN_WANTED_AS_IS;
-import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_PROBE_SPEC;
-import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.net.DnsResolver.FLAG_EMPTY;
-import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
-import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK;
-import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP;
-import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS;
-import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
-import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
-import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import static android.net.captiveportal.CaptivePortalProbeSpec.parseCaptivePortalProbeSpecs;
-import static android.net.metrics.ValidationProbeEvent.DNS_FAILURE;
-import static android.net.metrics.ValidationProbeEvent.DNS_SUCCESS;
-import static android.net.metrics.ValidationProbeEvent.PROBE_FALLBACK;
-import static android.net.metrics.ValidationProbeEvent.PROBE_PRIVDNS;
-import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD;
-import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_EVALUATION_TYPE;
-import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL;
-import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD;
-import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_DNS;
-import static android.net.util.DataStallUtils.DEFAULT_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD;
-import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_EVALUATION_TYPES;
-import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS;
-import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS;
-import static android.net.util.DataStallUtils.DEFAULT_DNS_LOG_SIZE;
-import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS;
-import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_URL;
-import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_HTTPS_URL;
-import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_HTTP_URL;
-import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_MODE;
-import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_MODE_IGNORE;
-import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_MODE_PROMPT;
-import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS;
-import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USER_AGENT;
-import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS;
-import static android.net.util.NetworkStackUtils.NAMESPACE_CONNECTIVITY;
-import static android.net.util.NetworkStackUtils.isEmpty;
-
-import static com.android.networkstack.util.DnsUtils.TYPE_ADDRCONFIG;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Resources;
-import android.net.ConnectivityManager;
-import android.net.DnsResolver;
-import android.net.INetworkMonitorCallbacks;
-import android.net.LinkProperties;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.ProxyInfo;
-import android.net.TrafficStats;
-import android.net.Uri;
-import android.net.captiveportal.CaptivePortalProbeResult;
-import android.net.captiveportal.CaptivePortalProbeSpec;
-import android.net.metrics.IpConnectivityLog;
-import android.net.metrics.NetworkEvent;
-import android.net.metrics.ValidationProbeEvent;
-import android.net.shared.NetworkMonitorUtils;
-import android.net.shared.PrivateDnsConfig;
-import android.net.util.NetworkStackUtils;
-import android.net.util.SharedLog;
-import android.net.util.Stopwatch;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
-import android.os.Bundle;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.telephony.AccessNetworkConstants;
-import android.telephony.CellSignalStrength;
-import android.telephony.NetworkRegistrationInfo;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-
-import androidx.annotation.ArrayRes;
-import androidx.annotation.StringRes;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.RingBufferIndices;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-import com.android.internal.util.TrafficStatsConstants;
-import com.android.networkstack.R;
-import com.android.networkstack.metrics.DataStallDetectionStats;
-import com.android.networkstack.metrics.DataStallStatsUtils;
-import com.android.networkstack.util.DnsUtils;
-
-import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.net.InetAddress;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Random;
-import java.util.UUID;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Function;
-
-/**
- * {@hide}
- */
-public class NetworkMonitor extends StateMachine {
- private static final String TAG = NetworkMonitor.class.getSimpleName();
- private static final boolean DBG = true;
- private static final boolean VDBG = false;
- private static final boolean VDBG_STALL = Log.isLoggable(TAG, Log.DEBUG);
- private static final String DEFAULT_USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) "
- + "AppleWebKit/537.36 (KHTML, like Gecko) "
- + "Chrome/60.0.3112.32 Safari/537.36";
-
- @VisibleForTesting
- static final String CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT =
- "captive_portal_dns_probe_timeout";
-
- private static final int SOCKET_TIMEOUT_MS = 10000;
- private static final int PROBE_TIMEOUT_MS = 3000;
-
- enum EvaluationResult {
- VALIDATED(true),
- CAPTIVE_PORTAL(false);
- final boolean mIsValidated;
- EvaluationResult(boolean isValidated) {
- this.mIsValidated = isValidated;
- }
- }
-
- enum ValidationStage {
- FIRST_VALIDATION(true),
- REVALIDATION(false);
- final boolean mIsFirstValidation;
- ValidationStage(boolean isFirstValidation) {
- this.mIsFirstValidation = isFirstValidation;
- }
- }
-
- /**
- * ConnectivityService has sent a notification to indicate that network has connected.
- * Initiates Network Validation.
- */
- private static final int CMD_NETWORK_CONNECTED = 1;
-
- /**
- * Message to self indicating it's time to evaluate a network's connectivity.
- * arg1 = Token to ignore old messages.
- */
- private static final int CMD_REEVALUATE = 6;
-
- /**
- * ConnectivityService has sent a notification to indicate that network has disconnected.
- */
- private static final int CMD_NETWORK_DISCONNECTED = 7;
-
- /**
- * Force evaluation even if it has succeeded in the past.
- * arg1 = UID responsible for requesting this reeval. Will be billed for data.
- */
- private static final int CMD_FORCE_REEVALUATION = 8;
-
- /**
- * Message to self indicating captive portal app finished.
- * arg1 = one of: APP_RETURN_DISMISSED,
- * APP_RETURN_UNWANTED,
- * APP_RETURN_WANTED_AS_IS
- * obj = mCaptivePortalLoggedInResponseToken as String
- */
- private static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = 9;
-
- /**
- * Message indicating sign-in app should be launched.
- * Sent by mLaunchCaptivePortalAppBroadcastReceiver when the
- * user touches the sign in notification, or sent by
- * ConnectivityService when the user touches the "sign into
- * network" button in the wifi access point detail page.
- */
- private static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = 11;
-
- /**
- * Retest network to see if captive portal is still in place.
- * arg1 = UID responsible for requesting this reeval. Will be billed for data.
- * 0 indicates self-initiated, so nobody to blame.
- */
- private static final int CMD_CAPTIVE_PORTAL_RECHECK = 12;
-
- /**
- * ConnectivityService notifies NetworkMonitor of settings changes to
- * Private DNS. If a DNS resolution is required, e.g. for DNS-over-TLS in
- * strict mode, then an event is sent back to ConnectivityService with the
- * result of the resolution attempt.
- *
- * A separate message is used to trigger (re)evaluation of the Private DNS
- * configuration, so that the message can be handled as needed in different
- * states, including being ignored until after an ongoing captive portal
- * validation phase is completed.
- */
- private static final int CMD_PRIVATE_DNS_SETTINGS_CHANGED = 13;
- private static final int CMD_EVALUATE_PRIVATE_DNS = 15;
-
- /**
- * Message to self indicating captive portal detection is completed.
- * obj = CaptivePortalProbeResult for detection result;
- */
- private static final int CMD_PROBE_COMPLETE = 16;
-
- /**
- * ConnectivityService notifies NetworkMonitor of DNS query responses event.
- * arg1 = returncode in OnDnsEvent which indicates the response code for the DNS query.
- */
- private static final int EVENT_DNS_NOTIFICATION = 17;
-
- /**
- * ConnectivityService notifies NetworkMonitor that the user accepts partial connectivity and
- * NetworkMonitor should ignore the https probe.
- */
- private static final int EVENT_ACCEPT_PARTIAL_CONNECTIVITY = 18;
-
- /**
- * ConnectivityService notifies NetworkMonitor of changed LinkProperties.
- * obj = new LinkProperties.
- */
- private static final int EVENT_LINK_PROPERTIES_CHANGED = 19;
-
- /**
- * ConnectivityService notifies NetworkMonitor of changed NetworkCapabilities.
- * obj = new NetworkCapabilities.
- */
- private static final int EVENT_NETWORK_CAPABILITIES_CHANGED = 20;
-
- // Start mReevaluateDelayMs at this value and double.
- private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
- private static final int MAX_REEVALUATE_DELAY_MS = 10 * 60 * 1000;
- // Before network has been evaluated this many times, ignore repeated reevaluate requests.
- private static final int IGNORE_REEVALUATE_ATTEMPTS = 5;
- private int mReevaluateToken = 0;
- private static final int NO_UID = 0;
- private static final int INVALID_UID = -1;
- private int mUidResponsibleForReeval = INVALID_UID;
- // Stop blaming UID that requested re-evaluation after this many attempts.
- private static final int BLAME_FOR_EVALUATION_ATTEMPTS = 5;
- // Delay between reevaluations once a captive portal has been found.
- private static final int CAPTIVE_PORTAL_REEVALUATE_DELAY_MS = 10 * 60 * 1000;
- private static final int NETWORK_VALIDATION_RESULT_INVALID = 0;
- private String mPrivateDnsProviderHostname = "";
-
- private final Context mContext;
- private final INetworkMonitorCallbacks mCallback;
- private final Network mCleartextDnsNetwork;
- private final Network mNetwork;
- private final TelephonyManager mTelephonyManager;
- private final WifiManager mWifiManager;
- private final ConnectivityManager mCm;
- private final IpConnectivityLog mMetricsLog;
- private final Dependencies mDependencies;
- private final DataStallStatsUtils mDetectionStatsUtils;
-
- // Configuration values for captive portal detection probes.
- private final String mCaptivePortalUserAgent;
- private final URL mCaptivePortalHttpsUrl;
- private final URL mCaptivePortalHttpUrl;
- private final URL[] mCaptivePortalFallbackUrls;
- @Nullable
- private final CaptivePortalProbeSpec[] mCaptivePortalFallbackSpecs;
-
- private NetworkCapabilities mNetworkCapabilities;
- private LinkProperties mLinkProperties;
-
- @VisibleForTesting
- protected boolean mIsCaptivePortalCheckEnabled;
-
- private boolean mUseHttps;
- // The total number of captive portal detection attempts for this NetworkMonitor instance.
- private int mValidations = 0;
-
- // Set if the user explicitly selected "Do not use this network" in captive portal sign-in app.
- private boolean mUserDoesNotWant = false;
- // Avoids surfacing "Sign in to network" notification.
- private boolean mDontDisplaySigninNotification = false;
-
- private final State mDefaultState = new DefaultState();
- private final State mValidatedState = new ValidatedState();
- private final State mMaybeNotifyState = new MaybeNotifyState();
- private final State mEvaluatingState = new EvaluatingState();
- private final State mCaptivePortalState = new CaptivePortalState();
- private final State mEvaluatingPrivateDnsState = new EvaluatingPrivateDnsState();
- private final State mProbingState = new ProbingState();
- private final State mWaitingForNextProbeState = new WaitingForNextProbeState();
-
- private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
-
- private final SharedLog mValidationLogs;
-
- private final Stopwatch mEvaluationTimer = new Stopwatch();
-
- // This variable is set before transitioning to the mCaptivePortalState.
- private CaptivePortalProbeResult mLastPortalProbeResult = CaptivePortalProbeResult.FAILED;
-
- // Random generator to select fallback URL index
- private final Random mRandom;
- private int mNextFallbackUrlIndex = 0;
-
-
- private int mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS;
- private int mEvaluateAttempts = 0;
- private volatile int mProbeToken = 0;
- private final int mConsecutiveDnsTimeoutThreshold;
- private final int mDataStallMinEvaluateTime;
- private final int mDataStallValidDnsTimeThreshold;
- private final int mDataStallEvaluationType;
- private final DnsStallDetector mDnsStallDetector;
- private long mLastProbeTime;
- // Set to true if data stall is suspected and reset to false after metrics are sent to statsd.
- private boolean mCollectDataStallMetrics;
- private boolean mAcceptPartialConnectivity = false;
- private final EvaluationState mEvaluationState = new EvaluationState();
-
- public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network,
- SharedLog validationLog) {
- this(context, cb, network, new IpConnectivityLog(), validationLog,
- Dependencies.DEFAULT, new DataStallStatsUtils());
- }
-
- @VisibleForTesting
- protected NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network,
- IpConnectivityLog logger, SharedLog validationLogs,
- Dependencies deps, DataStallStatsUtils detectionStatsUtils) {
- // Add suffix indicating which NetworkMonitor we're talking about.
- super(TAG + "/" + network.toString());
-
- // Logs with a tag of the form given just above, e.g.
- // <timestamp> 862 2402 D NetworkMonitor/NetworkAgentInfo [WIFI () - 100]: ...
- setDbg(VDBG);
-
- mContext = context;
- mMetricsLog = logger;
- mValidationLogs = validationLogs;
- mCallback = cb;
- mDependencies = deps;
- mDetectionStatsUtils = detectionStatsUtils;
- mNetwork = network;
- mCleartextDnsNetwork = deps.getPrivateDnsBypassNetwork(network);
- mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- mCm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
-
- // CHECKSTYLE:OFF IndentationCheck
- addState(mDefaultState);
- addState(mMaybeNotifyState, mDefaultState);
- addState(mEvaluatingState, mMaybeNotifyState);
- addState(mProbingState, mEvaluatingState);
- addState(mWaitingForNextProbeState, mEvaluatingState);
- addState(mCaptivePortalState, mMaybeNotifyState);
- addState(mEvaluatingPrivateDnsState, mDefaultState);
- addState(mValidatedState, mDefaultState);
- setInitialState(mDefaultState);
- // CHECKSTYLE:ON IndentationCheck
-
- mIsCaptivePortalCheckEnabled = getIsCaptivePortalCheckEnabled();
- mUseHttps = getUseHttpsValidation();
- mCaptivePortalUserAgent = getCaptivePortalUserAgent();
- mCaptivePortalHttpsUrl = makeURL(getCaptivePortalServerHttpsUrl());
- mCaptivePortalHttpUrl = makeURL(getCaptivePortalServerHttpUrl());
- mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls();
- mCaptivePortalFallbackSpecs = makeCaptivePortalFallbackProbeSpecs();
- mRandom = deps.getRandom();
- // TODO: Evaluate to move data stall configuration to a specific class.
- mConsecutiveDnsTimeoutThreshold = getConsecutiveDnsTimeoutThreshold();
- mDnsStallDetector = new DnsStallDetector(mConsecutiveDnsTimeoutThreshold);
- mDataStallMinEvaluateTime = getDataStallMinEvaluateTime();
- mDataStallValidDnsTimeThreshold = getDataStallValidDnsTimeThreshold();
- mDataStallEvaluationType = getDataStallEvaluationType();
-
- // Provide empty LinkProperties and NetworkCapabilities to make sure they are never null,
- // even before notifyNetworkConnected.
- mLinkProperties = new LinkProperties();
- mNetworkCapabilities = new NetworkCapabilities(null);
- }
-
- /**
- * ConnectivityService notifies NetworkMonitor that the user already accepted partial
- * connectivity previously, so NetworkMonitor can validate the network even if it has partial
- * connectivity.
- */
- public void setAcceptPartialConnectivity() {
- sendMessage(EVENT_ACCEPT_PARTIAL_CONNECTIVITY);
- }
-
- /**
- * Request the NetworkMonitor to reevaluate the network.
- */
- public void forceReevaluation(int responsibleUid) {
- sendMessage(CMD_FORCE_REEVALUATION, responsibleUid, 0);
- }
-
- /**
- * Send a notification to NetworkMonitor indicating that there was a DNS query response event.
- * @param returnCode the DNS return code of the response.
- */
- public void notifyDnsResponse(int returnCode) {
- sendMessage(EVENT_DNS_NOTIFICATION, returnCode);
- }
-
- /**
- * Send a notification to NetworkMonitor indicating that private DNS settings have changed.
- * @param newCfg The new private DNS configuration.
- */
- public void notifyPrivateDnsSettingsChanged(PrivateDnsConfig newCfg) {
- // Cancel any outstanding resolutions.
- removeMessages(CMD_PRIVATE_DNS_SETTINGS_CHANGED);
- // Send the update to the proper thread.
- sendMessage(CMD_PRIVATE_DNS_SETTINGS_CHANGED, newCfg);
- }
-
- /**
- * Send a notification to NetworkMonitor indicating that the network is now connected.
- */
- public void notifyNetworkConnected(LinkProperties lp, NetworkCapabilities nc) {
- sendMessage(CMD_NETWORK_CONNECTED, new Pair<>(
- new LinkProperties(lp), new NetworkCapabilities(nc)));
- }
-
- private void updateConnectedNetworkAttributes(Message connectedMsg) {
- final Pair<LinkProperties, NetworkCapabilities> attrs =
- (Pair<LinkProperties, NetworkCapabilities>) connectedMsg.obj;
- mLinkProperties = attrs.first;
- mNetworkCapabilities = attrs.second;
- }
-
- /**
- * Send a notification to NetworkMonitor indicating that the network is now disconnected.
- */
- public void notifyNetworkDisconnected() {
- sendMessage(CMD_NETWORK_DISCONNECTED);
- }
-
- /**
- * Send a notification to NetworkMonitor indicating that link properties have changed.
- */
- public void notifyLinkPropertiesChanged(final LinkProperties lp) {
- sendMessage(EVENT_LINK_PROPERTIES_CHANGED, new LinkProperties(lp));
- }
-
- /**
- * Send a notification to NetworkMonitor indicating that network capabilities have changed.
- */
- public void notifyNetworkCapabilitiesChanged(final NetworkCapabilities nc) {
- sendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED, new NetworkCapabilities(nc));
- }
-
- /**
- * Request the captive portal application to be launched.
- */
- public void launchCaptivePortalApp() {
- sendMessage(CMD_LAUNCH_CAPTIVE_PORTAL_APP);
- }
-
- /**
- * Notify that the captive portal app was closed with the provided response code.
- */
- public void notifyCaptivePortalAppFinished(int response) {
- sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response);
- }
-
- @Override
- protected void log(String s) {
- if (DBG) Log.d(TAG + "/" + mCleartextDnsNetwork.toString(), s);
- }
-
- private void validationLog(int probeType, Object url, String msg) {
- String probeName = ValidationProbeEvent.getProbeName(probeType);
- validationLog(String.format("%s %s %s", probeName, url, msg));
- }
-
- private void validationLog(String s) {
- if (DBG) log(s);
- mValidationLogs.log(s);
- }
-
- private ValidationStage validationStage() {
- return 0 == mValidations ? ValidationStage.FIRST_VALIDATION : ValidationStage.REVALIDATION;
- }
-
- private boolean isValidationRequired() {
- return NetworkMonitorUtils.isValidationRequired(mNetworkCapabilities);
- }
-
- private boolean isPrivateDnsValidationRequired() {
- return NetworkMonitorUtils.isPrivateDnsValidationRequired(mNetworkCapabilities);
- }
-
- private void notifyNetworkTested(int result, @Nullable String redirectUrl) {
- try {
- mCallback.notifyNetworkTested(result, redirectUrl);
- } catch (RemoteException e) {
- Log.e(TAG, "Error sending network test result", e);
- }
- }
-
- private void showProvisioningNotification(String action) {
- try {
- mCallback.showProvisioningNotification(action, mContext.getPackageName());
- } catch (RemoteException e) {
- Log.e(TAG, "Error showing provisioning notification", e);
- }
- }
-
- private void hideProvisioningNotification() {
- try {
- mCallback.hideProvisioningNotification();
- } catch (RemoteException e) {
- Log.e(TAG, "Error hiding provisioning notification", e);
- }
- }
-
- // DefaultState is the parent of all States. It exists only to handle CMD_* messages but
- // does not entail any real state (hence no enter() or exit() routines).
- private class DefaultState extends State {
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_NETWORK_CONNECTED:
- updateConnectedNetworkAttributes(message);
- logNetworkEvent(NetworkEvent.NETWORK_CONNECTED);
- transitionTo(mEvaluatingState);
- return HANDLED;
- case CMD_NETWORK_DISCONNECTED:
- logNetworkEvent(NetworkEvent.NETWORK_DISCONNECTED);
- quit();
- return HANDLED;
- case CMD_FORCE_REEVALUATION:
- case CMD_CAPTIVE_PORTAL_RECHECK:
- final int dnsCount = mDnsStallDetector.getConsecutiveTimeoutCount();
- validationLog("Forcing reevaluation for UID " + message.arg1
- + ". Dns signal count: " + dnsCount);
- mUidResponsibleForReeval = message.arg1;
- transitionTo(mEvaluatingState);
- return HANDLED;
- case CMD_CAPTIVE_PORTAL_APP_FINISHED:
- log("CaptivePortal App responded with " + message.arg1);
-
- // If the user has seen and acted on a captive portal notification, and the
- // captive portal app is now closed, disable HTTPS probes. This avoids the
- // following pathological situation:
- //
- // 1. HTTP probe returns a captive portal, HTTPS probe fails or times out.
- // 2. User opens the app and logs into the captive portal.
- // 3. HTTP starts working, but HTTPS still doesn't work for some other reason -
- // perhaps due to the network blocking HTTPS?
- //
- // In this case, we'll fail to validate the network even after the app is
- // dismissed. There is now no way to use this network, because the app is now
- // gone, so the user cannot select "Use this network as is".
- mUseHttps = false;
-
- switch (message.arg1) {
- case APP_RETURN_DISMISSED:
- sendMessage(CMD_FORCE_REEVALUATION, NO_UID, 0);
- break;
- case APP_RETURN_WANTED_AS_IS:
- mDontDisplaySigninNotification = true;
- // TODO: Distinguish this from a network that actually validates.
- // Displaying the "x" on the system UI icon may still be a good idea.
- transitionTo(mEvaluatingPrivateDnsState);
- break;
- case APP_RETURN_UNWANTED:
- mDontDisplaySigninNotification = true;
- mUserDoesNotWant = true;
- mEvaluationState.reportEvaluationResult(
- NETWORK_VALIDATION_RESULT_INVALID, null);
- // TODO: Should teardown network.
- mUidResponsibleForReeval = 0;
- transitionTo(mEvaluatingState);
- break;
- }
- return HANDLED;
- case CMD_PRIVATE_DNS_SETTINGS_CHANGED: {
- final PrivateDnsConfig cfg = (PrivateDnsConfig) message.obj;
- if (!isPrivateDnsValidationRequired() || cfg == null || !cfg.inStrictMode()) {
- // No DNS resolution required.
- //
- // We don't force any validation in opportunistic mode
- // here. Opportunistic mode nameservers are validated
- // separately within netd.
- //
- // Reset Private DNS settings state.
- mPrivateDnsProviderHostname = "";
- break;
- }
-
- mPrivateDnsProviderHostname = cfg.hostname;
-
- // DNS resolutions via Private DNS strict mode block for a
- // few seconds (~4.2) checking for any IP addresses to
- // arrive and validate. Initiating a (re)evaluation now
- // should not significantly alter the validation outcome.
- //
- // No matter what: enqueue a validation request; one of
- // three things can happen with this request:
- // [1] ignored (EvaluatingState or CaptivePortalState)
- // [2] transition to EvaluatingPrivateDnsState
- // (DefaultState and ValidatedState)
- // [3] handled (EvaluatingPrivateDnsState)
- //
- // The Private DNS configuration to be evaluated will:
- // [1] be skipped (not in strict mode), or
- // [2] validate (huzzah), or
- // [3] encounter some problem (invalid hostname,
- // no resolved IP addresses, IPs unreachable,
- // port 853 unreachable, port 853 is not running a
- // DNS-over-TLS server, et cetera).
- sendMessage(CMD_EVALUATE_PRIVATE_DNS);
- break;
- }
- case EVENT_DNS_NOTIFICATION:
- mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1);
- break;
- // Set mAcceptPartialConnectivity to true and if network start evaluating or
- // re-evaluating and get the result of partial connectivity, ProbingState will
- // disable HTTPS probe and transition to EvaluatingPrivateDnsState.
- case EVENT_ACCEPT_PARTIAL_CONNECTIVITY:
- maybeDisableHttpsProbing(true /* acceptPartial */);
- break;
- case EVENT_LINK_PROPERTIES_CHANGED:
- mLinkProperties = (LinkProperties) message.obj;
- break;
- case EVENT_NETWORK_CAPABILITIES_CHANGED:
- mNetworkCapabilities = (NetworkCapabilities) message.obj;
- break;
- default:
- break;
- }
- return HANDLED;
- }
- }
-
- // Being in the ValidatedState State indicates a Network is:
- // - Successfully validated, or
- // - Wanted "as is" by the user, or
- // - Does not satisfy the default NetworkRequest and so validation has been skipped.
- private class ValidatedState extends State {
- @Override
- public void enter() {
- maybeLogEvaluationResult(
- networkEventType(validationStage(), EvaluationResult.VALIDATED));
- // If the user has accepted partial connectivity and HTTPS probing is disabled, then
- // mark the network as validated and partial so that settings can keep informing the
- // user that the connection is limited.
- int result = NETWORK_VALIDATION_RESULT_VALID;
- if (!mUseHttps && mAcceptPartialConnectivity) {
- result |= NETWORK_VALIDATION_RESULT_PARTIAL;
- }
- mEvaluationState.reportEvaluationResult(result, null /* redirectUrl */);
- mValidations++;
- }
-
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_NETWORK_CONNECTED:
- updateConnectedNetworkAttributes(message);
- transitionTo(mValidatedState);
- break;
- case CMD_EVALUATE_PRIVATE_DNS:
- transitionTo(mEvaluatingPrivateDnsState);
- break;
- case EVENT_DNS_NOTIFICATION:
- mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1);
- if (isDataStall()) {
- mCollectDataStallMetrics = true;
- validationLog("Suspecting data stall, reevaluate");
- transitionTo(mEvaluatingState);
- }
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
-
- private void writeDataStallStats(@NonNull final CaptivePortalProbeResult result) {
- /*
- * Collect data stall detection level information for each transport type. Collect type
- * specific information for cellular and wifi only currently. Generate
- * DataStallDetectionStats for each transport type. E.g., if a network supports both
- * TRANSPORT_WIFI and TRANSPORT_VPN, two DataStallDetectionStats will be generated.
- */
- final int[] transports = mNetworkCapabilities.getTransportTypes();
-
- for (int i = 0; i < transports.length; i++) {
- DataStallStatsUtils.write(buildDataStallDetectionStats(transports[i]), result);
- }
- mCollectDataStallMetrics = false;
- }
-
- @VisibleForTesting
- protected DataStallDetectionStats buildDataStallDetectionStats(int transport) {
- final DataStallDetectionStats.Builder stats = new DataStallDetectionStats.Builder();
- if (VDBG_STALL) log("collectDataStallMetrics: type=" + transport);
- stats.setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS);
- stats.setNetworkType(transport);
- switch (transport) {
- case NetworkCapabilities.TRANSPORT_WIFI:
- // TODO: Update it if status query in dual wifi is supported.
- final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
- stats.setWiFiData(wifiInfo);
- break;
- case NetworkCapabilities.TRANSPORT_CELLULAR:
- final boolean isRoaming = !mNetworkCapabilities.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
- final SignalStrength ss = mTelephonyManager.getSignalStrength();
- // TODO(b/120452078): Support multi-sim.
- stats.setCellData(
- mTelephonyManager.getDataNetworkType(),
- isRoaming,
- mTelephonyManager.getNetworkOperator(),
- mTelephonyManager.getSimOperator(),
- (ss != null)
- ? ss.getLevel() : CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
- break;
- default:
- // No transport type specific information for the other types.
- break;
- }
- addDnsEvents(stats);
-
- return stats.build();
- }
-
- @VisibleForTesting
- protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) {
- final int size = mDnsStallDetector.mResultIndices.size();
- for (int i = 1; i <= DEFAULT_DNS_LOG_SIZE && i <= size; i++) {
- final int index = mDnsStallDetector.mResultIndices.indexOf(size - i);
- stats.addDnsEvent(mDnsStallDetector.mDnsEvents[index].mReturnCode,
- mDnsStallDetector.mDnsEvents[index].mTimeStamp);
- }
- }
-
-
- // Being in the MaybeNotifyState State indicates the user may have been notified that sign-in
- // is required. This State takes care to clear the notification upon exit from the State.
- private class MaybeNotifyState extends State {
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_LAUNCH_CAPTIVE_PORTAL_APP:
- final Bundle appExtras = new Bundle();
- // OneAddressPerFamilyNetwork is not parcelable across processes.
- final Network network = new Network(mCleartextDnsNetwork);
- appExtras.putParcelable(ConnectivityManager.EXTRA_NETWORK, network);
- final CaptivePortalProbeResult probeRes = mLastPortalProbeResult;
- appExtras.putString(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl);
- if (probeRes.probeSpec != null) {
- final String encodedSpec = probeRes.probeSpec.getEncodedSpec();
- appExtras.putString(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC, encodedSpec);
- }
- appExtras.putString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT,
- mCaptivePortalUserAgent);
- mCm.startCaptivePortalApp(network, appExtras);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- @Override
- public void exit() {
- if (mLaunchCaptivePortalAppBroadcastReceiver != null) {
- mContext.unregisterReceiver(mLaunchCaptivePortalAppBroadcastReceiver);
- mLaunchCaptivePortalAppBroadcastReceiver = null;
- }
- hideProvisioningNotification();
- }
- }
-
- // Being in the EvaluatingState State indicates the Network is being evaluated for internet
- // connectivity, or that the user has indicated that this network is unwanted.
- private class EvaluatingState extends State {
- @Override
- public void enter() {
- // If we have already started to track time spent in EvaluatingState
- // don't reset the timer due simply to, say, commands or events that
- // cause us to exit and re-enter EvaluatingState.
- if (!mEvaluationTimer.isStarted()) {
- mEvaluationTimer.start();
- }
- sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
- if (mUidResponsibleForReeval != INVALID_UID) {
- TrafficStats.setThreadStatsUid(mUidResponsibleForReeval);
- mUidResponsibleForReeval = INVALID_UID;
- }
- mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS;
- mEvaluateAttempts = 0;
- // Reset all current probe results to zero, but retain current validation state until
- // validation succeeds or fails.
- mEvaluationState.clearProbeResults();
- }
-
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_REEVALUATE:
- if (message.arg1 != mReevaluateToken || mUserDoesNotWant) {
- return HANDLED;
- }
- // Don't bother validating networks that don't satisfy the default request.
- // This includes:
- // - VPNs which can be considered explicitly desired by the user and the
- // user's desire trumps whether the network validates.
- // - Networks that don't provide Internet access. It's unclear how to
- // validate such networks.
- // - Untrusted networks. It's unsafe to prompt the user to sign-in to
- // such networks and the user didn't express interest in connecting to
- // such networks (an app did) so the user may be unhappily surprised when
- // asked to sign-in to a network they didn't want to connect to in the
- // first place. Validation could be done to adjust the network scores
- // however these networks are app-requested and may not be intended for
- // general usage, in which case general validation may not be an accurate
- // measure of the network's quality. Only the app knows how to evaluate
- // the network so don't bother validating here. Furthermore sending HTTP
- // packets over the network may be undesirable, for example an extremely
- // expensive metered network, or unwanted leaking of the User Agent string.
- //
- // On networks that need to support private DNS in strict mode (e.g., VPNs, but
- // not networks that don't provide Internet access), we still need to perform
- // private DNS server resolution.
- if (!isValidationRequired()) {
- if (isPrivateDnsValidationRequired()) {
- validationLog("Network would not satisfy default request, "
- + "resolving private DNS");
- transitionTo(mEvaluatingPrivateDnsState);
- } else {
- validationLog("Network would not satisfy default request, "
- + "not validating");
- transitionTo(mValidatedState);
- }
- return HANDLED;
- }
- mEvaluateAttempts++;
-
- transitionTo(mProbingState);
- return HANDLED;
- case CMD_FORCE_REEVALUATION:
- // Before IGNORE_REEVALUATE_ATTEMPTS attempts are made,
- // ignore any re-evaluation requests. After, restart the
- // evaluation process via EvaluatingState#enter.
- return (mEvaluateAttempts < IGNORE_REEVALUATE_ATTEMPTS) ? HANDLED : NOT_HANDLED;
- // Disable HTTPS probe and transition to EvaluatingPrivateDnsState because:
- // 1. Network is connected and finish the network validation.
- // 2. NetworkMonitor detects network is partial connectivity and user accepts it.
- case EVENT_ACCEPT_PARTIAL_CONNECTIVITY:
- maybeDisableHttpsProbing(true /* acceptPartial */);
- transitionTo(mEvaluatingPrivateDnsState);
- return HANDLED;
- default:
- return NOT_HANDLED;
- }
- }
-
- @Override
- public void exit() {
- TrafficStats.clearThreadStatsUid();
- }
- }
-
- // BroadcastReceiver that waits for a particular Intent and then posts a message.
- private class CustomIntentReceiver extends BroadcastReceiver {
- private final int mToken;
- private final int mWhat;
- private final String mAction;
- CustomIntentReceiver(String action, int token, int what) {
- mToken = token;
- mWhat = what;
- mAction = action + "_" + mCleartextDnsNetwork.getNetworkHandle() + "_" + token;
- mContext.registerReceiver(this, new IntentFilter(mAction));
- }
- public PendingIntent getPendingIntent() {
- final Intent intent = new Intent(mAction);
- intent.setPackage(mContext.getPackageName());
- return PendingIntent.getBroadcast(mContext, 0, intent, 0);
- }
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(mAction)) sendMessage(obtainMessage(mWhat, mToken));
- }
- }
-
- // Being in the CaptivePortalState State indicates a captive portal was detected and the user
- // has been shown a notification to sign-in.
- private class CaptivePortalState extends State {
- private static final String ACTION_LAUNCH_CAPTIVE_PORTAL_APP =
- "android.net.netmon.launchCaptivePortalApp";
-
- @Override
- public void enter() {
- maybeLogEvaluationResult(
- networkEventType(validationStage(), EvaluationResult.CAPTIVE_PORTAL));
- // Don't annoy user with sign-in notifications.
- if (mDontDisplaySigninNotification) return;
- // Create a CustomIntentReceiver that sends us a
- // CMD_LAUNCH_CAPTIVE_PORTAL_APP message when the user
- // touches the notification.
- if (mLaunchCaptivePortalAppBroadcastReceiver == null) {
- // Wait for result.
- mLaunchCaptivePortalAppBroadcastReceiver = new CustomIntentReceiver(
- ACTION_LAUNCH_CAPTIVE_PORTAL_APP, new Random().nextInt(),
- CMD_LAUNCH_CAPTIVE_PORTAL_APP);
- // Display the sign in notification.
- // Only do this once for every time we enter MaybeNotifyState. b/122164725
- showProvisioningNotification(mLaunchCaptivePortalAppBroadcastReceiver.mAction);
- }
- // Retest for captive portal occasionally.
- sendMessageDelayed(CMD_CAPTIVE_PORTAL_RECHECK, 0 /* no UID */,
- CAPTIVE_PORTAL_REEVALUATE_DELAY_MS);
- mValidations++;
- }
-
- @Override
- public void exit() {
- removeMessages(CMD_CAPTIVE_PORTAL_RECHECK);
- }
- }
-
- private class EvaluatingPrivateDnsState extends State {
- private int mPrivateDnsReevalDelayMs;
- private PrivateDnsConfig mPrivateDnsConfig;
-
- @Override
- public void enter() {
- mPrivateDnsReevalDelayMs = INITIAL_REEVALUATE_DELAY_MS;
- mPrivateDnsConfig = null;
- sendMessage(CMD_EVALUATE_PRIVATE_DNS);
- }
-
- @Override
- public boolean processMessage(Message msg) {
- switch (msg.what) {
- case CMD_EVALUATE_PRIVATE_DNS:
- if (inStrictMode()) {
- if (!isStrictModeHostnameResolved()) {
- resolveStrictModeHostname();
-
- if (isStrictModeHostnameResolved()) {
- notifyPrivateDnsConfigResolved();
- } else {
- handlePrivateDnsEvaluationFailure();
- break;
- }
- }
-
- // Look up a one-time hostname, to bypass caching.
- //
- // Note that this will race with ConnectivityService
- // code programming the DNS-over-TLS server IP addresses
- // into netd (if invoked, above). If netd doesn't know
- // the IP addresses yet, or if the connections to the IP
- // addresses haven't yet been validated, netd will block
- // for up to a few seconds before failing the lookup.
- if (!sendPrivateDnsProbe()) {
- handlePrivateDnsEvaluationFailure();
- break;
- }
- }
-
- // All good!
- transitionTo(mValidatedState);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
-
- private boolean inStrictMode() {
- return !TextUtils.isEmpty(mPrivateDnsProviderHostname);
- }
-
- private boolean isStrictModeHostnameResolved() {
- return (mPrivateDnsConfig != null)
- && mPrivateDnsConfig.hostname.equals(mPrivateDnsProviderHostname)
- && (mPrivateDnsConfig.ips.length > 0);
- }
-
- private void resolveStrictModeHostname() {
- try {
- // Do a blocking DNS resolution using the network-assigned nameservers.
- final InetAddress[] ips = DnsUtils.getAllByName(mDependencies.getDnsResolver(),
- mCleartextDnsNetwork, mPrivateDnsProviderHostname, getDnsProbeTimeout());
- mPrivateDnsConfig = new PrivateDnsConfig(mPrivateDnsProviderHostname, ips);
- validationLog("Strict mode hostname resolved: " + mPrivateDnsConfig);
- } catch (UnknownHostException uhe) {
- mPrivateDnsConfig = null;
- validationLog("Strict mode hostname resolution failed: " + uhe.getMessage());
- }
- mEvaluationState.reportProbeResult(NETWORK_VALIDATION_PROBE_PRIVDNS,
- (mPrivateDnsConfig != null) /* succeeded */);
- }
-
- private void notifyPrivateDnsConfigResolved() {
- try {
- mCallback.notifyPrivateDnsConfigResolved(mPrivateDnsConfig.toParcel());
- } catch (RemoteException e) {
- Log.e(TAG, "Error sending private DNS config resolved notification", e);
- }
- }
-
- private void handlePrivateDnsEvaluationFailure() {
- mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_INVALID,
- null /* redirectUrl */);
- // Queue up a re-evaluation with backoff.
- //
- // TODO: Consider abandoning this state after a few attempts and
- // transitioning back to EvaluatingState, to perhaps give ourselves
- // the opportunity to (re)detect a captive portal or something.
- //
- // TODO: distinguish between CMD_EVALUATE_PRIVATE_DNS messages that are caused by server
- // lookup failures (which should continue to do exponential backoff) and
- // CMD_EVALUATE_PRIVATE_DNS messages that are caused by user reconfiguration (which
- // should be processed immediately.
- sendMessageDelayed(CMD_EVALUATE_PRIVATE_DNS, mPrivateDnsReevalDelayMs);
- mPrivateDnsReevalDelayMs *= 2;
- if (mPrivateDnsReevalDelayMs > MAX_REEVALUATE_DELAY_MS) {
- mPrivateDnsReevalDelayMs = MAX_REEVALUATE_DELAY_MS;
- }
- }
-
- private boolean sendPrivateDnsProbe() {
- // q.v. system/netd/server/dns/DnsTlsTransport.cpp
- final String oneTimeHostnameSuffix = "-dnsotls-ds.metric.gstatic.com";
- final String host = UUID.randomUUID().toString().substring(0, 8)
- + oneTimeHostnameSuffix;
- final Stopwatch watch = new Stopwatch().start();
- boolean success = false;
- long time;
- try {
- final InetAddress[] ips = mNetwork.getAllByName(host);
- time = watch.stop();
- final String strIps = Arrays.toString(ips);
- success = (ips != null && ips.length > 0);
- validationLog(PROBE_PRIVDNS, host, String.format("%dms: %s", time, strIps));
- } catch (UnknownHostException uhe) {
- time = watch.stop();
- validationLog(PROBE_PRIVDNS, host,
- String.format("%dms - Error: %s", time, uhe.getMessage()));
- }
- logValidationProbe(time, PROBE_PRIVDNS, success ? DNS_SUCCESS : DNS_FAILURE);
- mEvaluationState.reportProbeResult(NETWORK_VALIDATION_PROBE_PRIVDNS, success);
- return success;
- }
- }
-
- private class ProbingState extends State {
- private Thread mThread;
-
- @Override
- public void enter() {
- if (mEvaluateAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
- //Don't continue to blame UID forever.
- TrafficStats.clearThreadStatsUid();
- }
-
- final int token = ++mProbeToken;
- mThread = new Thread(() -> sendMessage(obtainMessage(CMD_PROBE_COMPLETE, token, 0,
- isCaptivePortal())));
- mThread.start();
- }
-
- @Override
- public boolean processMessage(Message message) {
- switch (message.what) {
- case CMD_PROBE_COMPLETE:
- // Ensure that CMD_PROBE_COMPLETE from stale threads are ignored.
- if (message.arg1 != mProbeToken) {
- return HANDLED;
- }
-
- final CaptivePortalProbeResult probeResult =
- (CaptivePortalProbeResult) message.obj;
- mLastProbeTime = SystemClock.elapsedRealtime();
-
- if (mCollectDataStallMetrics) {
- writeDataStallStats(probeResult);
- }
-
- if (probeResult.isSuccessful()) {
- // Transit EvaluatingPrivateDnsState to get to Validated
- // state (even if no Private DNS validation required).
- transitionTo(mEvaluatingPrivateDnsState);
- } else if (probeResult.isPortal()) {
- mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_INVALID,
- probeResult.redirectUrl);
- mLastPortalProbeResult = probeResult;
- transitionTo(mCaptivePortalState);
- } else if (probeResult.isPartialConnectivity()) {
- mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_PARTIAL,
- null /* redirectUrl */);
- // Check if disable https probing needed.
- maybeDisableHttpsProbing(mAcceptPartialConnectivity);
- if (mAcceptPartialConnectivity) {
- transitionTo(mEvaluatingPrivateDnsState);
- } else {
- transitionTo(mWaitingForNextProbeState);
- }
- } else {
- logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
- mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_INVALID,
- null /* redirectUrl */);
- transitionTo(mWaitingForNextProbeState);
- }
- return HANDLED;
- case EVENT_DNS_NOTIFICATION:
- case EVENT_ACCEPT_PARTIAL_CONNECTIVITY:
- // Leave the event to DefaultState.
- return NOT_HANDLED;
- default:
- // Wait for probe result and defer events to next state by default.
- deferMessage(message);
- return HANDLED;
- }
- }
-
- @Override
- public void exit() {
- if (mThread.isAlive()) {
- mThread.interrupt();
- }
- mThread = null;
- }
- }
-
- // Being in the WaitingForNextProbeState indicates that evaluating probes failed and state is
- // transited from ProbingState. This ensures that the state machine is only in ProbingState
- // while a probe is in progress, not while waiting to perform the next probe. That allows
- // ProbingState to defer most messages until the probe is complete, which keeps the code simple
- // and matches the pre-Q behaviour where probes were a blocking operation performed on the state
- // machine thread.
- private class WaitingForNextProbeState extends State {
- @Override
- public void enter() {
- scheduleNextProbe();
- }
-
- private void scheduleNextProbe() {
- final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
- sendMessageDelayed(msg, mReevaluateDelayMs);
- mReevaluateDelayMs *= 2;
- if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) {
- mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS;
- }
- }
-
- @Override
- public boolean processMessage(Message message) {
- return NOT_HANDLED;
- }
- }
-
- // Limits the list of IP addresses returned by getAllByName or tried by openConnection to at
- // most one per address family. This ensures we only wait up to 20 seconds for TCP connections
- // to complete, regardless of how many IP addresses a host has.
- private static class OneAddressPerFamilyNetwork extends Network {
- OneAddressPerFamilyNetwork(Network network) {
- // Always bypass Private DNS.
- super(network.getPrivateDnsBypassingCopy());
- }
-
- @Override
- public InetAddress[] getAllByName(String host) throws UnknownHostException {
- final List<InetAddress> addrs = Arrays.asList(super.getAllByName(host));
-
- // Ensure the address family of the first address is tried first.
- LinkedHashMap<Class, InetAddress> addressByFamily = new LinkedHashMap<>();
- addressByFamily.put(addrs.get(0).getClass(), addrs.get(0));
- Collections.shuffle(addrs);
-
- for (InetAddress addr : addrs) {
- addressByFamily.put(addr.getClass(), addr);
- }
-
- return addressByFamily.values().toArray(new InetAddress[addressByFamily.size()]);
- }
- }
-
- private boolean getIsCaptivePortalCheckEnabled() {
- String symbol = CAPTIVE_PORTAL_MODE;
- int defaultValue = CAPTIVE_PORTAL_MODE_PROMPT;
- int mode = mDependencies.getSetting(mContext, symbol, defaultValue);
- return mode != CAPTIVE_PORTAL_MODE_IGNORE;
- }
-
- private boolean getUseHttpsValidation() {
- return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY,
- CAPTIVE_PORTAL_USE_HTTPS, 1) == 1;
- }
-
- private String getCaptivePortalServerHttpsUrl() {
- return getSettingFromResource(mContext, R.string.config_captive_portal_https_url,
- R.string.default_captive_portal_https_url, CAPTIVE_PORTAL_HTTPS_URL);
- }
-
- private int getDnsProbeTimeout() {
- return getIntSetting(mContext, R.integer.config_captive_portal_dns_probe_timeout,
- CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT,
- R.integer.default_captive_portal_dns_probe_timeout);
- }
-
- /**
- * Gets an integer setting from resources or device config
- *
- * configResource is used if set, followed by device config if set, followed by defaultResource.
- * If none of these are set then an exception is thrown.
- *
- * TODO: move to a common location such as a ConfigUtils class.
- * TODO(b/130324939): test that the resources can be overlayed by an RRO package.
- */
- @VisibleForTesting
- int getIntSetting(@NonNull final Context context, @StringRes int configResource,
- @NonNull String symbol, @StringRes int defaultResource) {
- final Resources res = context.getResources();
- try {
- return res.getInteger(configResource);
- } catch (Resources.NotFoundException e) {
- return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY,
- symbol, res.getInteger(defaultResource));
- }
- }
-
- /**
- * Get the captive portal server HTTP URL that is configured on the device.
- *
- * NetworkMonitor does not use {@link ConnectivityManager#getCaptivePortalServerUrl()} as
- * it has its own updatable strategies to detect captive portals. The framework only advises
- * on one URL that can be used, while NetworkMonitor may implement more complex logic.
- */
- public String getCaptivePortalServerHttpUrl() {
- return getSettingFromResource(mContext, R.string.config_captive_portal_http_url,
- R.string.default_captive_portal_http_url, CAPTIVE_PORTAL_HTTP_URL);
- }
-
- private int getConsecutiveDnsTimeoutThreshold() {
- return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY,
- CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD,
- DEFAULT_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD);
- }
-
- private int getDataStallMinEvaluateTime() {
- return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY,
- CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL,
- DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS);
- }
-
- private int getDataStallValidDnsTimeThreshold() {
- return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY,
- CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD,
- DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS);
- }
-
- private int getDataStallEvaluationType() {
- return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY,
- CONFIG_DATA_STALL_EVALUATION_TYPE,
- DEFAULT_DATA_STALL_EVALUATION_TYPES);
- }
-
- private URL[] makeCaptivePortalFallbackUrls() {
- try {
- final String firstUrl = mDependencies.getSetting(mContext, CAPTIVE_PORTAL_FALLBACK_URL,
- null);
-
- final URL[] settingProviderUrls;
- if (!TextUtils.isEmpty(firstUrl)) {
- final String otherUrls = mDependencies.getDeviceConfigProperty(
- NAMESPACE_CONNECTIVITY, CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, "");
- // otherUrls may be empty, but .split() ignores trailing empty strings
- final String separator = ",";
- final String[] urls = (firstUrl + separator + otherUrls).split(separator);
- settingProviderUrls = convertStrings(urls, this::makeURL, new URL[0]);
- } else {
- settingProviderUrls = new URL[0];
- }
-
- return getArrayConfig(settingProviderUrls, R.array.config_captive_portal_fallback_urls,
- R.array.default_captive_portal_fallback_urls, this::makeURL);
- } catch (Exception e) {
- // Don't let a misconfiguration bootloop the system.
- Log.e(TAG, "Error parsing configured fallback URLs", e);
- return new URL[0];
- }
- }
-
- private CaptivePortalProbeSpec[] makeCaptivePortalFallbackProbeSpecs() {
- try {
- final String settingsValue = mDependencies.getDeviceConfigProperty(
- NAMESPACE_CONNECTIVITY, CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS, null);
-
- final CaptivePortalProbeSpec[] emptySpecs = new CaptivePortalProbeSpec[0];
- final CaptivePortalProbeSpec[] providerValue = TextUtils.isEmpty(settingsValue)
- ? emptySpecs
- : parseCaptivePortalProbeSpecs(settingsValue).toArray(emptySpecs);
-
- return getArrayConfig(providerValue, R.array.config_captive_portal_fallback_probe_specs,
- R.array.default_captive_portal_fallback_probe_specs,
- CaptivePortalProbeSpec::parseSpecOrNull);
- } catch (Exception e) {
- // Don't let a misconfiguration bootloop the system.
- Log.e(TAG, "Error parsing configured fallback probe specs", e);
- return null;
- }
- }
-
- /**
- * Read a setting from a resource or the settings provider.
- *
- * <p>The configuration resource is prioritized, then the provider value, then the default
- * resource value.
- * @param context The context
- * @param configResource The resource id for the configuration parameter
- * @param defaultResource The resource id for the default value
- * @param symbol The symbol in the settings provider
- * @return The best available value
- */
- @NonNull
- private String getSettingFromResource(@NonNull final Context context,
- @StringRes int configResource, @StringRes int defaultResource,
- @NonNull String symbol) {
- final Resources res = context.getResources();
- String setting = res.getString(configResource);
-
- if (!TextUtils.isEmpty(setting)) return setting;
-
- setting = mDependencies.getSetting(context, symbol, null);
- if (!TextUtils.isEmpty(setting)) return setting;
-
- return res.getString(defaultResource);
- }
-
- /**
- * Get an array configuration from resources or the settings provider.
- *
- * <p>The configuration resource is prioritized, then the provider values, then the default
- * resource values.
- * @param providerValue Values obtained from the setting provider.
- * @param configResId ID of the configuration resource.
- * @param defaultResId ID of the default resource.
- * @param resourceConverter Converter from the resource strings to stored setting class. Null
- * return values are ignored.
- */
- private <T> T[] getArrayConfig(@NonNull T[] providerValue, @ArrayRes int configResId,
- @ArrayRes int defaultResId, @NonNull Function<String, T> resourceConverter) {
- final Resources res = mContext.getResources();
- String[] configValue = res.getStringArray(configResId);
-
- if (configValue.length == 0) {
- if (providerValue.length > 0) {
- return providerValue;
- }
-
- configValue = res.getStringArray(defaultResId);
- }
-
- return convertStrings(configValue, resourceConverter, Arrays.copyOf(providerValue, 0));
- }
-
- /**
- * Convert a String array to an array of some other type using the specified converter.
- *
- * <p>Any null value, or value for which the converter throws a {@link RuntimeException}, will
- * not be added to the output array, so the output array may be smaller than the input.
- */
- private <T> T[] convertStrings(
- @NonNull String[] strings, Function<String, T> converter, T[] emptyArray) {
- final ArrayList<T> convertedValues = new ArrayList<>(strings.length);
- for (String configString : strings) {
- T convertedValue = null;
- try {
- convertedValue = converter.apply(configString);
- } catch (Exception e) {
- Log.e(TAG, "Error parsing configuration", e);
- // Fall through
- }
- if (convertedValue != null) {
- convertedValues.add(convertedValue);
- }
- }
- return convertedValues.toArray(emptyArray);
- }
-
- private String getCaptivePortalUserAgent() {
- return mDependencies.getDeviceConfigProperty(NAMESPACE_CONNECTIVITY,
- CAPTIVE_PORTAL_USER_AGENT, DEFAULT_USER_AGENT);
- }
-
- private URL nextFallbackUrl() {
- if (mCaptivePortalFallbackUrls.length == 0) {
- return null;
- }
- int idx = Math.abs(mNextFallbackUrlIndex) % mCaptivePortalFallbackUrls.length;
- mNextFallbackUrlIndex += mRandom.nextInt(); // randomly change url without memory.
- return mCaptivePortalFallbackUrls[idx];
- }
-
- private CaptivePortalProbeSpec nextFallbackSpec() {
- if (isEmpty(mCaptivePortalFallbackSpecs)) {
- return null;
- }
- // Randomly change spec without memory. Also randomize the first attempt.
- final int idx = Math.abs(mRandom.nextInt()) % mCaptivePortalFallbackSpecs.length;
- return mCaptivePortalFallbackSpecs[idx];
- }
-
- @VisibleForTesting
- protected CaptivePortalProbeResult isCaptivePortal() {
- if (!mIsCaptivePortalCheckEnabled) {
- validationLog("Validation disabled.");
- return CaptivePortalProbeResult.SUCCESS;
- }
-
- URL pacUrl = null;
- URL httpsUrl = mCaptivePortalHttpsUrl;
- URL httpUrl = mCaptivePortalHttpUrl;
-
- // On networks with a PAC instead of fetching a URL that should result in a 204
- // response, we instead simply fetch the PAC script. This is done for a few reasons:
- // 1. At present our PAC code does not yet handle multiple PACs on multiple networks
- // until something like https://android-review.googlesource.com/#/c/115180/ lands.
- // Network.openConnection() will ignore network-specific PACs and instead fetch
- // using NO_PROXY. If a PAC is in place, the only fetch we know will succeed with
- // NO_PROXY is the fetch of the PAC itself.
- // 2. To proxy the generate_204 fetch through a PAC would require a number of things
- // happen before the fetch can commence, namely:
- // a) the PAC script be fetched
- // b) a PAC script resolver service be fired up and resolve the captive portal
- // server.
- // Network validation could be delayed until these prerequisities are satisifed or
- // could simply be left to race them. Neither is an optimal solution.
- // 3. PAC scripts are sometimes used to block or restrict Internet access and may in
- // fact block fetching of the generate_204 URL which would lead to false negative
- // results for network validation.
- final ProxyInfo proxyInfo = mLinkProperties.getHttpProxy();
- if (proxyInfo != null && !Uri.EMPTY.equals(proxyInfo.getPacFileUrl())) {
- pacUrl = makeURL(proxyInfo.getPacFileUrl().toString());
- if (pacUrl == null) {
- return CaptivePortalProbeResult.FAILED;
- }
- }
-
- if ((pacUrl == null) && (httpUrl == null || httpsUrl == null)) {
- return CaptivePortalProbeResult.FAILED;
- }
-
- long startTime = SystemClock.elapsedRealtime();
-
- final CaptivePortalProbeResult result;
- if (pacUrl != null) {
- result = sendDnsAndHttpProbes(null, pacUrl, ValidationProbeEvent.PROBE_PAC);
- reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, result);
- } else if (mUseHttps) {
- // Probe results are reported inside sendParallelHttpProbes.
- result = sendParallelHttpProbes(proxyInfo, httpsUrl, httpUrl);
- } else {
- result = sendDnsAndHttpProbes(proxyInfo, httpUrl, ValidationProbeEvent.PROBE_HTTP);
- reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, result);
- }
-
- long endTime = SystemClock.elapsedRealtime();
-
- sendNetworkConditionsBroadcast(true /* response received */,
- result.isPortal() /* isCaptivePortal */,
- startTime, endTime);
-
- log("isCaptivePortal: isSuccessful()=" + result.isSuccessful()
- + " isPortal()=" + result.isPortal()
- + " RedirectUrl=" + result.redirectUrl
- + " isPartialConnectivity()=" + result.isPartialConnectivity()
- + " Time=" + (endTime - startTime) + "ms");
-
- return result;
- }
-
- /**
- * Do a DNS resolution and URL fetch on a known web server to see if we get the data we expect.
- * @return a CaptivePortalProbeResult inferred from the HTTP response.
- */
- private CaptivePortalProbeResult sendDnsAndHttpProbes(ProxyInfo proxy, URL url, int probeType) {
- // Pre-resolve the captive portal server host so we can log it.
- // Only do this if HttpURLConnection is about to, to avoid any potentially
- // unnecessary resolution.
- final String host = (proxy != null) ? proxy.getHost() : url.getHost();
- // This method cannot safely report probe results because it might not be running on the
- // state machine thread. Reporting results here would cause races and potentially send
- // information to callers that does not make sense because the state machine has already
- // changed state.
- sendDnsProbe(host);
- return sendHttpProbe(url, probeType, null);
- }
-
- /** Do a DNS lookup for the given server, or throw UnknownHostException after timeoutMs */
- @VisibleForTesting
- protected InetAddress[] sendDnsProbeWithTimeout(String host, int timeoutMs)
- throws UnknownHostException {
- return DnsUtils.getAllByName(mDependencies.getDnsResolver(), mCleartextDnsNetwork, host,
- TYPE_ADDRCONFIG, FLAG_EMPTY, timeoutMs);
- }
-
- /** Do a DNS resolution of the given server. */
- private void sendDnsProbe(String host) {
- if (TextUtils.isEmpty(host)) {
- return;
- }
-
- final String name = ValidationProbeEvent.getProbeName(ValidationProbeEvent.PROBE_DNS);
- final Stopwatch watch = new Stopwatch().start();
- int result;
- String connectInfo;
- try {
- InetAddress[] addresses = sendDnsProbeWithTimeout(host, getDnsProbeTimeout());
- StringBuffer buffer = new StringBuffer();
- for (InetAddress address : addresses) {
- buffer.append(',').append(address.getHostAddress());
- }
- result = ValidationProbeEvent.DNS_SUCCESS;
- connectInfo = "OK " + buffer.substring(1);
- } catch (UnknownHostException e) {
- result = ValidationProbeEvent.DNS_FAILURE;
- connectInfo = "FAIL";
- }
- final long latency = watch.stop();
- validationLog(ValidationProbeEvent.PROBE_DNS, host,
- String.format("%dms %s", latency, connectInfo));
- logValidationProbe(latency, ValidationProbeEvent.PROBE_DNS, result);
- }
-
- /**
- * Do a URL fetch on a known web server to see if we get the data we expect.
- * @return a CaptivePortalProbeResult inferred from the HTTP response.
- */
- @VisibleForTesting
- protected CaptivePortalProbeResult sendHttpProbe(URL url, int probeType,
- @Nullable CaptivePortalProbeSpec probeSpec) {
- HttpURLConnection urlConnection = null;
- int httpResponseCode = CaptivePortalProbeResult.FAILED_CODE;
- String redirectUrl = null;
- final Stopwatch probeTimer = new Stopwatch().start();
- final int oldTag = TrafficStats.getAndSetThreadStatsTag(
- TrafficStatsConstants.TAG_SYSTEM_PROBE);
- try {
- urlConnection = (HttpURLConnection) mCleartextDnsNetwork.openConnection(url);
- urlConnection.setInstanceFollowRedirects(probeType == ValidationProbeEvent.PROBE_PAC);
- urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
- urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
- urlConnection.setRequestProperty("Connection", "close");
- urlConnection.setUseCaches(false);
- if (mCaptivePortalUserAgent != null) {
- urlConnection.setRequestProperty("User-Agent", mCaptivePortalUserAgent);
- }
- // cannot read request header after connection
- String requestHeader = urlConnection.getRequestProperties().toString();
-
- // Time how long it takes to get a response to our request
- long requestTimestamp = SystemClock.elapsedRealtime();
-
- httpResponseCode = urlConnection.getResponseCode();
- redirectUrl = urlConnection.getHeaderField("location");
-
- // Time how long it takes to get a response to our request
- long responseTimestamp = SystemClock.elapsedRealtime();
-
- validationLog(probeType, url, "time=" + (responseTimestamp - requestTimestamp) + "ms"
- + " ret=" + httpResponseCode
- + " request=" + requestHeader
- + " headers=" + urlConnection.getHeaderFields());
- // NOTE: We may want to consider an "HTTP/1.0 204" response to be a captive
- // portal. The only example of this seen so far was a captive portal. For
- // the time being go with prior behavior of assuming it's not a captive
- // portal. If it is considered a captive portal, a different sign-in URL
- // is needed (i.e. can't browse a 204). This could be the result of an HTTP
- // proxy server.
- if (httpResponseCode == 200) {
- long contentLength = urlConnection.getContentLengthLong();
- if (probeType == ValidationProbeEvent.PROBE_PAC) {
- validationLog(
- probeType, url, "PAC fetch 200 response interpreted as 204 response.");
- httpResponseCode = CaptivePortalProbeResult.SUCCESS_CODE;
- } else if (contentLength == -1) {
- // When no Content-length (default value == -1), attempt to read a byte
- // from the response. Do not use available() as it is unreliable.
- // See http://b/33498325.
- if (urlConnection.getInputStream().read() == -1) {
- validationLog(probeType, url,
- "Empty 200 response interpreted as failed response.");
- httpResponseCode = CaptivePortalProbeResult.FAILED_CODE;
- }
- } else if (contentLength <= 4) {
- // Consider 200 response with "Content-length <= 4" to not be a captive
- // portal. There's no point in considering this a captive portal as the
- // user cannot sign-in to an empty page. Probably the result of a broken
- // transparent proxy. See http://b/9972012 and http://b/122999481.
- validationLog(probeType, url, "200 response with Content-length <= 4"
- + " interpreted as failed response.");
- httpResponseCode = CaptivePortalProbeResult.FAILED_CODE;
- }
- }
- } catch (IOException e) {
- validationLog(probeType, url, "Probe failed with exception " + e);
- if (httpResponseCode == CaptivePortalProbeResult.FAILED_CODE) {
- // TODO: Ping gateway and DNS server and log results.
- }
- } finally {
- if (urlConnection != null) {
- urlConnection.disconnect();
- }
- TrafficStats.setThreadStatsTag(oldTag);
- }
- logValidationProbe(probeTimer.stop(), probeType, httpResponseCode);
-
- if (probeSpec == null) {
- return new CaptivePortalProbeResult(httpResponseCode, redirectUrl, url.toString());
- } else {
- return probeSpec.getResult(httpResponseCode, redirectUrl);
- }
- }
-
- private CaptivePortalProbeResult sendParallelHttpProbes(
- ProxyInfo proxy, URL httpsUrl, URL httpUrl) {
- // Number of probes to wait for. If a probe completes with a conclusive answer
- // it shortcuts the latch immediately by forcing the count to 0.
- final CountDownLatch latch = new CountDownLatch(2);
-
- final class ProbeThread extends Thread {
- private final boolean mIsHttps;
- private volatile CaptivePortalProbeResult mResult = CaptivePortalProbeResult.FAILED;
-
- ProbeThread(boolean isHttps) {
- mIsHttps = isHttps;
- }
-
- public CaptivePortalProbeResult result() {
- return mResult;
- }
-
- @Override
- public void run() {
- if (mIsHttps) {
- mResult =
- sendDnsAndHttpProbes(proxy, httpsUrl, ValidationProbeEvent.PROBE_HTTPS);
- } else {
- mResult = sendDnsAndHttpProbes(proxy, httpUrl, ValidationProbeEvent.PROBE_HTTP);
- }
- if ((mIsHttps && mResult.isSuccessful()) || (!mIsHttps && mResult.isPortal())) {
- // Stop waiting immediately if https succeeds or if http finds a portal.
- while (latch.getCount() > 0) {
- latch.countDown();
- }
- }
- // Signal this probe has completed.
- latch.countDown();
- }
- }
-
- final ProbeThread httpsProbe = new ProbeThread(true);
- final ProbeThread httpProbe = new ProbeThread(false);
-
- try {
- httpsProbe.start();
- httpProbe.start();
- latch.await(PROBE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- validationLog("Error: probes wait interrupted!");
- return CaptivePortalProbeResult.FAILED;
- }
-
- final CaptivePortalProbeResult httpsResult = httpsProbe.result();
- final CaptivePortalProbeResult httpResult = httpProbe.result();
-
- // Look for a conclusive probe result first.
- if (httpResult.isPortal()) {
- reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, httpResult);
- return httpResult;
- }
- // httpsResult.isPortal() is not expected, but check it nonetheless.
- if (httpsResult.isPortal() || httpsResult.isSuccessful()) {
- reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTPS, httpsResult);
- return httpsResult;
- }
- // If a fallback method exists, use it to retry portal detection.
- // If we have new-style probe specs, use those. Otherwise, use the fallback URLs.
- final CaptivePortalProbeSpec probeSpec = nextFallbackSpec();
- final URL fallbackUrl = (probeSpec != null) ? probeSpec.getUrl() : nextFallbackUrl();
- CaptivePortalProbeResult fallbackProbeResult = null;
- if (fallbackUrl != null) {
- fallbackProbeResult = sendHttpProbe(fallbackUrl, PROBE_FALLBACK, probeSpec);
- reportHttpProbeResult(NETWORK_VALIDATION_PROBE_FALLBACK, fallbackProbeResult);
- if (fallbackProbeResult.isPortal()) {
- return fallbackProbeResult;
- }
- }
- // Otherwise wait until http and https probes completes and use their results.
- try {
- httpProbe.join();
- reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, httpProbe.result());
-
- if (httpProbe.result().isPortal()) {
- return httpProbe.result();
- }
-
- httpsProbe.join();
- reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTPS, httpsProbe.result());
-
- final boolean isHttpSuccessful =
- (httpProbe.result().isSuccessful()
- || (fallbackProbeResult != null && fallbackProbeResult.isSuccessful()));
- if (httpsProbe.result().isFailed() && isHttpSuccessful) {
- return CaptivePortalProbeResult.PARTIAL;
- }
- return httpsProbe.result();
- } catch (InterruptedException e) {
- validationLog("Error: http or https probe wait interrupted!");
- return CaptivePortalProbeResult.FAILED;
- }
- }
-
- private URL makeURL(String url) {
- if (url != null) {
- try {
- return new URL(url);
- } catch (MalformedURLException e) {
- validationLog("Bad URL: " + url);
- }
- }
- return null;
- }
-
- /**
- * @param responseReceived - whether or not we received a valid HTTP response to our request.
- * If false, isCaptivePortal and responseTimestampMs are ignored
- * TODO: This should be moved to the transports. The latency could be passed to the transports
- * along with the captive portal result. Currently the TYPE_MOBILE broadcasts appear unused so
- * perhaps this could just be added to the WiFi transport only.
- */
- private void sendNetworkConditionsBroadcast(boolean responseReceived, boolean isCaptivePortal,
- long requestTimestampMs, long responseTimestampMs) {
- Intent latencyBroadcast =
- new Intent(NetworkMonitorUtils.ACTION_NETWORK_CONDITIONS_MEASURED);
- if (mNetworkCapabilities.hasTransport(TRANSPORT_WIFI)) {
- if (!mWifiManager.isScanAlwaysAvailable()) {
- return;
- }
-
- WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo();
- if (currentWifiInfo != null) {
- // NOTE: getSSID()'s behavior changed in API 17; before that, SSIDs were not
- // surrounded by double quotation marks (thus violating the Javadoc), but this
- // was changed to match the Javadoc in API 17. Since clients may have started
- // sanitizing the output of this method since API 17 was released, we should
- // not change it here as it would become impossible to tell whether the SSID is
- // simply being surrounded by quotes due to the API, or whether those quotes
- // are actually part of the SSID.
- latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_SSID,
- currentWifiInfo.getSSID());
- latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_BSSID,
- currentWifiInfo.getBSSID());
- } else {
- if (VDBG) logw("network info is TYPE_WIFI but no ConnectionInfo found");
- return;
- }
- latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_WIFI);
- } else if (mNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
- // TODO(b/123893112): Support multi-sim.
- latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_NETWORK_TYPE,
- mTelephonyManager.getNetworkType());
- final ServiceState dataSs = mTelephonyManager.getServiceState();
- if (dataSs == null) {
- logw("failed to retrieve ServiceState");
- return;
- }
- // See if the data sub is registered for PS services on cell.
- final NetworkRegistrationInfo nri = dataSs.getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS,
- AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- latencyBroadcast.putExtra(
- NetworkMonitorUtils.EXTRA_CELL_ID,
- nri == null ? null : nri.getCellIdentity());
- latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_MOBILE);
- } else {
- return;
- }
- latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_RESPONSE_RECEIVED,
- responseReceived);
- latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_REQUEST_TIMESTAMP_MS,
- requestTimestampMs);
-
- if (responseReceived) {
- latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_IS_CAPTIVE_PORTAL,
- isCaptivePortal);
- latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_RESPONSE_TIMESTAMP_MS,
- responseTimestampMs);
- }
- mContext.sendBroadcastAsUser(latencyBroadcast, UserHandle.CURRENT,
- NetworkMonitorUtils.PERMISSION_ACCESS_NETWORK_CONDITIONS);
- }
-
- private void logNetworkEvent(int evtype) {
- int[] transports = mNetworkCapabilities.getTransportTypes();
- mMetricsLog.log(mCleartextDnsNetwork, transports, new NetworkEvent(evtype));
- }
-
- private int networkEventType(ValidationStage s, EvaluationResult r) {
- if (s.mIsFirstValidation) {
- if (r.mIsValidated) {
- return NetworkEvent.NETWORK_FIRST_VALIDATION_SUCCESS;
- } else {
- return NetworkEvent.NETWORK_FIRST_VALIDATION_PORTAL_FOUND;
- }
- } else {
- if (r.mIsValidated) {
- return NetworkEvent.NETWORK_REVALIDATION_SUCCESS;
- } else {
- return NetworkEvent.NETWORK_REVALIDATION_PORTAL_FOUND;
- }
- }
- }
-
- private void maybeLogEvaluationResult(int evtype) {
- if (mEvaluationTimer.isRunning()) {
- int[] transports = mNetworkCapabilities.getTransportTypes();
- mMetricsLog.log(mCleartextDnsNetwork, transports,
- new NetworkEvent(evtype, mEvaluationTimer.stop()));
- mEvaluationTimer.reset();
- }
- }
-
- private void logValidationProbe(long durationMs, int probeType, int probeResult) {
- int[] transports = mNetworkCapabilities.getTransportTypes();
- boolean isFirstValidation = validationStage().mIsFirstValidation;
- ValidationProbeEvent ev = new ValidationProbeEvent.Builder()
- .setProbeType(probeType, isFirstValidation)
- .setReturnCode(probeResult)
- .setDurationMs(durationMs)
- .build();
- mMetricsLog.log(mCleartextDnsNetwork, transports, ev);
- }
-
- @VisibleForTesting
- static class Dependencies {
- public Network getPrivateDnsBypassNetwork(Network network) {
- return new OneAddressPerFamilyNetwork(network);
- }
-
- public DnsResolver getDnsResolver() {
- return DnsResolver.getInstance();
- }
-
- public Random getRandom() {
- return new Random();
- }
-
- /**
- * Get the value of a global integer setting.
- * @param symbol Name of the setting
- * @param defaultValue Value to return if the setting is not defined.
- */
- public int getSetting(Context context, String symbol, int defaultValue) {
- return Settings.Global.getInt(context.getContentResolver(), symbol, defaultValue);
- }
-
- /**
- * Get the value of a global String setting.
- * @param symbol Name of the setting
- * @param defaultValue Value to return if the setting is not defined.
- */
- public String getSetting(Context context, String symbol, String defaultValue) {
- final String value = Settings.Global.getString(context.getContentResolver(), symbol);
- return value != null ? value : defaultValue;
- }
-
- /**
- * Look up the value of a property in DeviceConfig.
- * @param namespace The namespace containing the property to look up.
- * @param name The name of the property to look up.
- * @param defaultValue The value to return if the property does not exist or has no non-null
- * value.
- * @return the corresponding value, or defaultValue if none exists.
- */
- @Nullable
- public String getDeviceConfigProperty(@NonNull String namespace, @NonNull String name,
- @Nullable String defaultValue) {
- return NetworkStackUtils.getDeviceConfigProperty(namespace, name, defaultValue);
- }
-
- /**
- * Look up the value of a property in DeviceConfig.
- * @param namespace The namespace containing the property to look up.
- * @param name The name of the property to look up.
- * @param defaultValue The value to return if the property does not exist or has no non-null
- * value.
- * @return the corresponding value, or defaultValue if none exists.
- */
- public int getDeviceConfigPropertyInt(@NonNull String namespace, @NonNull String name,
- int defaultValue) {
- return NetworkStackUtils.getDeviceConfigPropertyInt(namespace, name, defaultValue);
- }
-
- public static final Dependencies DEFAULT = new Dependencies();
- }
-
- /**
- * Methods in this class perform no locking because all accesses are performed on the state
- * machine's thread. Need to consider the thread safety if it ever could be accessed outside the
- * state machine.
- */
- @VisibleForTesting
- protected class DnsStallDetector {
- private int mConsecutiveTimeoutCount = 0;
- private int mSize;
- final DnsResult[] mDnsEvents;
- final RingBufferIndices mResultIndices;
-
- DnsStallDetector(int size) {
- mSize = Math.max(DEFAULT_DNS_LOG_SIZE, size);
- mDnsEvents = new DnsResult[mSize];
- mResultIndices = new RingBufferIndices(mSize);
- }
-
- @VisibleForTesting
- protected void accumulateConsecutiveDnsTimeoutCount(int code) {
- final DnsResult result = new DnsResult(code);
- mDnsEvents[mResultIndices.add()] = result;
- if (result.isTimeout()) {
- mConsecutiveTimeoutCount++;
- } else {
- // Keep the event in mDnsEvents without clearing it so that there are logs to do the
- // simulation and analysis.
- mConsecutiveTimeoutCount = 0;
- }
- }
-
- private boolean isDataStallSuspected(int timeoutCountThreshold, int validTime) {
- if (timeoutCountThreshold <= 0) {
- Log.wtf(TAG, "Timeout count threshold should be larger than 0.");
- return false;
- }
-
- // Check if the consecutive timeout count reach the threshold or not.
- if (mConsecutiveTimeoutCount < timeoutCountThreshold) {
- return false;
- }
-
- // Check if the target dns event index is valid or not.
- final int firstConsecutiveTimeoutIndex =
- mResultIndices.indexOf(mResultIndices.size() - timeoutCountThreshold);
-
- // If the dns timeout events happened long time ago, the events are meaningless for
- // data stall evaluation. Thus, check if the first consecutive timeout dns event
- // considered in the evaluation happened in defined threshold time.
- final long now = SystemClock.elapsedRealtime();
- final long firstTimeoutTime = now - mDnsEvents[firstConsecutiveTimeoutIndex].mTimeStamp;
- return (firstTimeoutTime < validTime);
- }
-
- int getConsecutiveTimeoutCount() {
- return mConsecutiveTimeoutCount;
- }
- }
-
- private static class DnsResult {
- // TODO: Need to move the DNS return code definition to a specific class once unify DNS
- // response code is done.
- private static final int RETURN_CODE_DNS_TIMEOUT = 255;
-
- private final long mTimeStamp;
- private final int mReturnCode;
-
- DnsResult(int code) {
- mTimeStamp = SystemClock.elapsedRealtime();
- mReturnCode = code;
- }
-
- private boolean isTimeout() {
- return mReturnCode == RETURN_CODE_DNS_TIMEOUT;
- }
- }
-
-
- @VisibleForTesting
- protected DnsStallDetector getDnsStallDetector() {
- return mDnsStallDetector;
- }
-
- private boolean dataStallEvaluateTypeEnabled(int type) {
- return (mDataStallEvaluationType & type) != 0;
- }
-
- @VisibleForTesting
- protected long getLastProbeTime() {
- return mLastProbeTime;
- }
-
- @VisibleForTesting
- protected boolean isDataStall() {
- boolean result = false;
- // Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the
- // possible traffic cost in metered network.
- if (!mNetworkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)
- && (SystemClock.elapsedRealtime() - getLastProbeTime()
- < mDataStallMinEvaluateTime)) {
- return false;
- }
-
- // Check dns signal. Suspect it may be a data stall if both :
- // 1. The number of consecutive DNS query timeouts >= mConsecutiveDnsTimeoutThreshold.
- // 2. Those consecutive DNS queries happened in the last mValidDataStallDnsTimeThreshold ms.
- if (dataStallEvaluateTypeEnabled(DATA_STALL_EVALUATION_TYPE_DNS)) {
- if (mDnsStallDetector.isDataStallSuspected(mConsecutiveDnsTimeoutThreshold,
- mDataStallValidDnsTimeThreshold)) {
- result = true;
- logNetworkEvent(NetworkEvent.NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND);
- }
- }
-
- if (VDBG_STALL) {
- log("isDataStall: result=" + result + ", consecutive dns timeout count="
- + mDnsStallDetector.getConsecutiveTimeoutCount());
- }
-
- return result;
- }
-
- // Class to keep state of evaluation results and probe results.
- // The main purpose is to ensure NetworkMonitor can notify ConnectivityService of probe results
- // as soon as they happen, without triggering any other changes. This requires keeping state on
- // the most recent evaluation result. Calling reportProbeResult will ensure that the results
- // reported to ConnectivityService contain the previous evaluation result, and thus won't
- // trigger a validation or partial connectivity state change.
- @VisibleForTesting
- protected class EvaluationState {
- // The latest validation result for this network. This is a bitmask of
- // INetworkMonitor.NETWORK_VALIDATION_RESULT_* constants.
- private int mEvaluationResult = NETWORK_VALIDATION_RESULT_INVALID;
- // Indicates which probes have completed since clearProbeResults was called.
- // This is a bitmask of INetworkMonitor.NETWORK_VALIDATION_PROBE_* constants.
- private int mProbeResults = 0;
- // The latest redirect URL.
- private String mRedirectUrl;
-
- protected void clearProbeResults() {
- mProbeResults = 0;
- }
-
- // Probe result for http probe should be updated from reportHttpProbeResult().
- protected void reportProbeResult(int probeResult, boolean succeeded) {
- if (succeeded) {
- mProbeResults |= probeResult;
- } else {
- mProbeResults &= ~probeResult;
- }
- notifyNetworkTested(getNetworkTestResult(), mRedirectUrl);
- }
-
- protected void reportEvaluationResult(int result, @Nullable String redirectUrl) {
- mEvaluationResult = result;
- mRedirectUrl = redirectUrl;
- notifyNetworkTested(getNetworkTestResult(), mRedirectUrl);
- }
-
- protected int getNetworkTestResult() {
- return mEvaluationResult | mProbeResults;
- }
- }
-
- @VisibleForTesting
- protected EvaluationState getEvaluationState() {
- return mEvaluationState;
- }
-
- private void maybeDisableHttpsProbing(boolean acceptPartial) {
- mAcceptPartialConnectivity = acceptPartial;
- // Ignore https probe in next validation if user accept partial connectivity on a partial
- // connectivity network.
- if (((mEvaluationState.getNetworkTestResult() & NETWORK_VALIDATION_RESULT_PARTIAL) != 0)
- && mAcceptPartialConnectivity) {
- mUseHttps = false;
- }
- }
-
- // Report HTTP, HTTP or FALLBACK probe result.
- @VisibleForTesting
- protected void reportHttpProbeResult(int probeResult,
- @NonNull final CaptivePortalProbeResult result) {
- boolean succeeded = result.isSuccessful();
- // The success of a HTTP probe does not tell us whether the DNS probe succeeded.
- // The DNS and HTTP probes run one after the other in sendDnsAndHttpProbes, and that
- // method cannot report the result of the DNS probe because that it could be running
- // on a different thread which is racing with the main state machine thread. So, if
- // an HTTP or HTTPS probe succeeded, assume that the DNS probe succeeded. But if an
- // HTTP or HTTPS probe failed, don't assume that DNS is not working.
- // TODO: fix this.
- if (succeeded) {
- probeResult |= NETWORK_VALIDATION_PROBE_DNS;
- }
- mEvaluationState.reportProbeResult(probeResult, succeeded);
- }
-}
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java
deleted file mode 100644
index a538a5b..0000000
--- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java
+++ /dev/null
@@ -1,703 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.connectivity.ipmemorystore;
-
-import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH;
-import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteCursor;
-import android.database.sqlite.SQLiteCursorDriver;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteQuery;
-import android.net.ipmemorystore.NetworkAttributes;
-import android.net.ipmemorystore.Status;
-import android.util.Log;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.StringJoiner;
-
-/**
- * Encapsulating class for using the SQLite database backing the memory store.
- *
- * This class groups together the contracts and the SQLite helper used to
- * use the database.
- *
- * @hide
- */
-public class IpMemoryStoreDatabase {
- private static final String TAG = IpMemoryStoreDatabase.class.getSimpleName();
- // A pair of NetworkAttributes objects is group-close if the confidence that they are
- // the same is above this cutoff. See NetworkAttributes and SameL3NetworkResponse.
- private static final float GROUPCLOSE_CONFIDENCE = 0.5f;
-
- /**
- * Contract class for the Network Attributes table.
- */
- public static class NetworkAttributesContract {
- public static final String TABLENAME = "NetworkAttributes";
-
- public static final String COLNAME_L2KEY = "l2Key";
- public static final String COLTYPE_L2KEY = "TEXT NOT NULL";
-
- public static final String COLNAME_EXPIRYDATE = "expiryDate";
- // Milliseconds since the Epoch, in true Java style
- public static final String COLTYPE_EXPIRYDATE = "BIGINT";
-
- public static final String COLNAME_ASSIGNEDV4ADDRESS = "assignedV4Address";
- public static final String COLTYPE_ASSIGNEDV4ADDRESS = "INTEGER";
-
- public static final String COLNAME_ASSIGNEDV4ADDRESSEXPIRY = "assignedV4AddressExpiry";
- // The lease expiry timestamp in uint of milliseconds
- public static final String COLTYPE_ASSIGNEDV4ADDRESSEXPIRY = "BIGINT";
-
- // Please note that the group hint is only a *hint*, hence its name. The client can offer
- // this information to nudge the grouping in the decision it thinks is right, but it can't
- // decide for the memory store what is the same L3 network.
- public static final String COLNAME_GROUPHINT = "groupHint";
- public static final String COLTYPE_GROUPHINT = "TEXT";
-
- public static final String COLNAME_DNSADDRESSES = "dnsAddresses";
- // Stored in marshalled form as is
- public static final String COLTYPE_DNSADDRESSES = "BLOB";
-
- public static final String COLNAME_MTU = "mtu";
- public static final String COLTYPE_MTU = "INTEGER DEFAULT -1";
-
- public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS "
- + TABLENAME + " ("
- + COLNAME_L2KEY + " " + COLTYPE_L2KEY + " PRIMARY KEY NOT NULL, "
- + COLNAME_EXPIRYDATE + " " + COLTYPE_EXPIRYDATE + ", "
- + COLNAME_ASSIGNEDV4ADDRESS + " " + COLTYPE_ASSIGNEDV4ADDRESS + ", "
- + COLNAME_ASSIGNEDV4ADDRESSEXPIRY + " " + COLTYPE_ASSIGNEDV4ADDRESSEXPIRY + ", "
- + COLNAME_GROUPHINT + " " + COLTYPE_GROUPHINT + ", "
- + COLNAME_DNSADDRESSES + " " + COLTYPE_DNSADDRESSES + ", "
- + COLNAME_MTU + " " + COLTYPE_MTU + ")";
- public static final String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLENAME;
- }
-
- /**
- * Contract class for the Private Data table.
- */
- public static class PrivateDataContract {
- public static final String TABLENAME = "PrivateData";
-
- public static final String COLNAME_L2KEY = "l2Key";
- public static final String COLTYPE_L2KEY = "TEXT NOT NULL";
-
- public static final String COLNAME_CLIENT = "client";
- public static final String COLTYPE_CLIENT = "TEXT NOT NULL";
-
- public static final String COLNAME_DATANAME = "dataName";
- public static final String COLTYPE_DATANAME = "TEXT NOT NULL";
-
- public static final String COLNAME_DATA = "data";
- public static final String COLTYPE_DATA = "BLOB NOT NULL";
-
- public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS "
- + TABLENAME + " ("
- + COLNAME_L2KEY + " " + COLTYPE_L2KEY + ", "
- + COLNAME_CLIENT + " " + COLTYPE_CLIENT + ", "
- + COLNAME_DATANAME + " " + COLTYPE_DATANAME + ", "
- + COLNAME_DATA + " " + COLTYPE_DATA + ", "
- + "PRIMARY KEY ("
- + COLNAME_L2KEY + ", "
- + COLNAME_CLIENT + ", "
- + COLNAME_DATANAME + "))";
- public static final String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLENAME;
- }
-
- // To save memory when the DB is not used, close it after 30s of inactivity. This is
- // determined manually based on what feels right.
- private static final long IDLE_CONNECTION_TIMEOUT_MS = 30_000;
-
- /** The SQLite DB helper */
- public static class DbHelper extends SQLiteOpenHelper {
- // Update this whenever changing the schema.
- private static final int SCHEMA_VERSION = 4;
- private static final String DATABASE_FILENAME = "IpMemoryStore.db";
- private static final String TRIGGER_NAME = "delete_cascade_to_private";
-
- public DbHelper(@NonNull final Context context) {
- super(context, DATABASE_FILENAME, null, SCHEMA_VERSION);
- setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
- }
-
- /** Called when the database is created */
- @Override
- public void onCreate(@NonNull final SQLiteDatabase db) {
- db.execSQL(NetworkAttributesContract.CREATE_TABLE);
- db.execSQL(PrivateDataContract.CREATE_TABLE);
- createTrigger(db);
- }
-
- /** Called when the database is upgraded */
- @Override
- public void onUpgrade(@NonNull final SQLiteDatabase db, final int oldVersion,
- final int newVersion) {
- try {
- if (oldVersion < 2) {
- // upgrade from version 1 to version 2
- // since we starts from version 2, do nothing here
- }
-
- if (oldVersion < 3) {
- // upgrade from version 2 to version 3
- final String sqlUpgradeAddressExpiry = "alter table"
- + " " + NetworkAttributesContract.TABLENAME + " ADD"
- + " " + NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESSEXPIRY
- + " " + NetworkAttributesContract.COLTYPE_ASSIGNEDV4ADDRESSEXPIRY;
- db.execSQL(sqlUpgradeAddressExpiry);
- }
-
- if (oldVersion < 4) {
- createTrigger(db);
- }
- } catch (SQLiteException e) {
- Log.e(TAG, "Could not upgrade to the new version", e);
- // create database with new version
- db.execSQL(NetworkAttributesContract.DROP_TABLE);
- db.execSQL(PrivateDataContract.DROP_TABLE);
- onCreate(db);
- }
- }
-
- /** Called when the database is downgraded */
- @Override
- public void onDowngrade(@NonNull final SQLiteDatabase db, final int oldVersion,
- final int newVersion) {
- // Downgrades always nuke all data and recreate an empty table.
- db.execSQL(NetworkAttributesContract.DROP_TABLE);
- db.execSQL(PrivateDataContract.DROP_TABLE);
- db.execSQL("DROP TRIGGER " + TRIGGER_NAME);
- onCreate(db);
- }
-
- private void createTrigger(@NonNull final SQLiteDatabase db) {
- final String createTrigger = "CREATE TRIGGER " + TRIGGER_NAME
- + " DELETE ON " + NetworkAttributesContract.TABLENAME
- + " BEGIN"
- + " DELETE FROM " + PrivateDataContract.TABLENAME + " WHERE OLD."
- + NetworkAttributesContract.COLNAME_L2KEY
- + "=" + PrivateDataContract.COLNAME_L2KEY
- + "; END;";
- db.execSQL(createTrigger);
- }
- }
-
- @NonNull
- private static byte[] encodeAddressList(@NonNull final List<InetAddress> addresses) {
- final ByteArrayOutputStream os = new ByteArrayOutputStream();
- for (final InetAddress address : addresses) {
- final byte[] b = address.getAddress();
- os.write(b.length);
- os.write(b, 0, b.length);
- }
- return os.toByteArray();
- }
-
- @NonNull
- private static ArrayList<InetAddress> decodeAddressList(@NonNull final byte[] encoded) {
- final ByteArrayInputStream is = new ByteArrayInputStream(encoded);
- final ArrayList<InetAddress> addresses = new ArrayList<>();
- int d = -1;
- while ((d = is.read()) != -1) {
- final byte[] bytes = new byte[d];
- is.read(bytes, 0, d);
- try {
- addresses.add(InetAddress.getByAddress(bytes));
- } catch (UnknownHostException e) { /* Hopefully impossible */ }
- }
- return addresses;
- }
-
- @NonNull
- private static ContentValues toContentValues(@Nullable final NetworkAttributes attributes) {
- final ContentValues values = new ContentValues();
- if (null == attributes) return values;
- if (null != attributes.assignedV4Address) {
- values.put(NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESS,
- inet4AddressToIntHTH(attributes.assignedV4Address));
- }
- if (null != attributes.assignedV4AddressExpiry) {
- values.put(NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESSEXPIRY,
- attributes.assignedV4AddressExpiry);
- }
- if (null != attributes.groupHint) {
- values.put(NetworkAttributesContract.COLNAME_GROUPHINT, attributes.groupHint);
- }
- if (null != attributes.dnsAddresses) {
- values.put(NetworkAttributesContract.COLNAME_DNSADDRESSES,
- encodeAddressList(attributes.dnsAddresses));
- }
- if (null != attributes.mtu) {
- values.put(NetworkAttributesContract.COLNAME_MTU, attributes.mtu);
- }
- return values;
- }
-
- // Convert a NetworkAttributes object to content values to store them in a table compliant
- // with the contract defined in NetworkAttributesContract.
- @NonNull
- private static ContentValues toContentValues(@NonNull final String key,
- @Nullable final NetworkAttributes attributes, final long expiry) {
- final ContentValues values = toContentValues(attributes);
- values.put(NetworkAttributesContract.COLNAME_L2KEY, key);
- values.put(NetworkAttributesContract.COLNAME_EXPIRYDATE, expiry);
- return values;
- }
-
- // Convert a byte array into content values to store it in a table compliant with the
- // contract defined in PrivateDataContract.
- @NonNull
- private static ContentValues toContentValues(@NonNull final String key,
- @NonNull final String clientId, @NonNull final String name,
- @NonNull final byte[] data) {
- final ContentValues values = new ContentValues();
- values.put(PrivateDataContract.COLNAME_L2KEY, key);
- values.put(PrivateDataContract.COLNAME_CLIENT, clientId);
- values.put(PrivateDataContract.COLNAME_DATANAME, name);
- values.put(PrivateDataContract.COLNAME_DATA, data);
- return values;
- }
-
- @Nullable
- private static NetworkAttributes readNetworkAttributesLine(@NonNull final Cursor cursor) {
- // Make sure the data hasn't expired
- final long expiry = getLong(cursor, NetworkAttributesContract.COLNAME_EXPIRYDATE, -1L);
- if (expiry < System.currentTimeMillis()) return null;
-
- final NetworkAttributes.Builder builder = new NetworkAttributes.Builder();
- final int assignedV4AddressInt = getInt(cursor,
- NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESS, 0);
- final long assignedV4AddressExpiry = getLong(cursor,
- NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESSEXPIRY, 0);
- final String groupHint = getString(cursor, NetworkAttributesContract.COLNAME_GROUPHINT);
- final byte[] dnsAddressesBlob =
- getBlob(cursor, NetworkAttributesContract.COLNAME_DNSADDRESSES);
- final int mtu = getInt(cursor, NetworkAttributesContract.COLNAME_MTU, -1);
- if (0 != assignedV4AddressInt) {
- builder.setAssignedV4Address(intToInet4AddressHTH(assignedV4AddressInt));
- }
- if (0 != assignedV4AddressExpiry) {
- builder.setAssignedV4AddressExpiry(assignedV4AddressExpiry);
- }
- builder.setGroupHint(groupHint);
- if (null != dnsAddressesBlob) {
- builder.setDnsAddresses(decodeAddressList(dnsAddressesBlob));
- }
- if (mtu >= 0) {
- builder.setMtu(mtu);
- }
- return builder.build();
- }
-
- private static final String[] EXPIRY_COLUMN = new String[] {
- NetworkAttributesContract.COLNAME_EXPIRYDATE
- };
- static final int EXPIRY_ERROR = -1; // Legal values for expiry are positive
-
- static final String SELECT_L2KEY = NetworkAttributesContract.COLNAME_L2KEY + " = ?";
-
- // Returns the expiry date of the specified row, or one of the error codes above if the
- // row is not found or some other error
- static long getExpiry(@NonNull final SQLiteDatabase db, @NonNull final String key) {
- final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME,
- EXPIRY_COLUMN, // columns
- SELECT_L2KEY, // selection
- new String[] { key }, // selectionArgs
- null, // groupBy
- null, // having
- null // orderBy
- );
- // L2KEY is the primary key ; it should not be possible to get more than one
- // result here. 0 results means the key was not found.
- if (cursor.getCount() != 1) return EXPIRY_ERROR;
- cursor.moveToFirst();
- final long result = cursor.getLong(0); // index in the EXPIRY_COLUMN array
- cursor.close();
- return result;
- }
-
- static final int RELEVANCE_ERROR = -1; // Legal values for relevance are positive
-
- // Returns the relevance of the specified row, or one of the error codes above if the
- // row is not found or some other error
- static int getRelevance(@NonNull final SQLiteDatabase db, @NonNull final String key) {
- final long expiry = getExpiry(db, key);
- return expiry < 0 ? (int) expiry : RelevanceUtils.computeRelevanceForNow(expiry);
- }
-
- // If the attributes are null, this will only write the expiry.
- // Returns an int out of Status.{SUCCESS, ERROR_*}
- static int storeNetworkAttributes(@NonNull final SQLiteDatabase db, @NonNull final String key,
- final long expiry, @Nullable final NetworkAttributes attributes) {
- final ContentValues cv = toContentValues(key, attributes, expiry);
- db.beginTransaction();
- try {
- // Unfortunately SQLite does not have any way to do INSERT OR UPDATE. Options are
- // to either insert with on conflict ignore then update (like done here), or to
- // construct a custom SQL INSERT statement with nested select.
- final long resultId = db.insertWithOnConflict(NetworkAttributesContract.TABLENAME,
- null, cv, SQLiteDatabase.CONFLICT_IGNORE);
- if (resultId < 0) {
- db.update(NetworkAttributesContract.TABLENAME, cv, SELECT_L2KEY, new String[]{key});
- }
- db.setTransactionSuccessful();
- return Status.SUCCESS;
- } catch (SQLiteException e) {
- // No space left on disk or something
- Log.e(TAG, "Could not write to the memory store", e);
- } finally {
- db.endTransaction();
- }
- return Status.ERROR_STORAGE;
- }
-
- // Returns an int out of Status.{SUCCESS, ERROR_*}
- static int storeBlob(@NonNull final SQLiteDatabase db, @NonNull final String key,
- @NonNull final String clientId, @NonNull final String name,
- @NonNull final byte[] data) {
- final long res = db.insertWithOnConflict(PrivateDataContract.TABLENAME, null,
- toContentValues(key, clientId, name, data), SQLiteDatabase.CONFLICT_REPLACE);
- return (res == -1) ? Status.ERROR_STORAGE : Status.SUCCESS;
- }
-
- @Nullable
- static NetworkAttributes retrieveNetworkAttributes(@NonNull final SQLiteDatabase db,
- @NonNull final String key) {
- final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME,
- null, // columns, null means everything
- NetworkAttributesContract.COLNAME_L2KEY + " = ?", // selection
- new String[] { key }, // selectionArgs
- null, // groupBy
- null, // having
- null); // orderBy
- // L2KEY is the primary key ; it should not be possible to get more than one
- // result here. 0 results means the key was not found.
- if (cursor.getCount() != 1) return null;
- cursor.moveToFirst();
- final NetworkAttributes attributes = readNetworkAttributesLine(cursor);
- cursor.close();
- return attributes;
- }
-
- private static final String[] DATA_COLUMN = new String[] {
- PrivateDataContract.COLNAME_DATA
- };
-
- @Nullable
- static byte[] retrieveBlob(@NonNull final SQLiteDatabase db, @NonNull final String key,
- @NonNull final String clientId, @NonNull final String name) {
- final Cursor cursor = db.query(PrivateDataContract.TABLENAME,
- DATA_COLUMN, // columns
- PrivateDataContract.COLNAME_L2KEY + " = ? AND " // selection
- + PrivateDataContract.COLNAME_CLIENT + " = ? AND "
- + PrivateDataContract.COLNAME_DATANAME + " = ?",
- new String[] { key, clientId, name }, // selectionArgs
- null, // groupBy
- null, // having
- null); // orderBy
- // The query above is querying by (composite) primary key, so it should not be possible to
- // get more than one result here. 0 results means the key was not found.
- if (cursor.getCount() != 1) return null;
- cursor.moveToFirst();
- final byte[] result = cursor.getBlob(0); // index in the DATA_COLUMN array
- cursor.close();
- return result;
- }
-
- /**
- * Wipe all data in tables when network factory reset occurs.
- */
- static void wipeDataUponNetworkReset(@NonNull final SQLiteDatabase db) {
- for (int remainingRetries = 3; remainingRetries > 0; --remainingRetries) {
- db.beginTransaction();
- try {
- db.delete(NetworkAttributesContract.TABLENAME, null, null);
- db.delete(PrivateDataContract.TABLENAME, null, null);
- final Cursor cursorNetworkAttributes = db.query(
- // table name
- NetworkAttributesContract.TABLENAME,
- // column name
- new String[] { NetworkAttributesContract.COLNAME_L2KEY },
- null, // selection
- null, // selectionArgs
- null, // groupBy
- null, // having
- null, // orderBy
- "1"); // limit
- if (0 != cursorNetworkAttributes.getCount()) {
- cursorNetworkAttributes.close();
- continue;
- }
- cursorNetworkAttributes.close();
- final Cursor cursorPrivateData = db.query(
- // table name
- PrivateDataContract.TABLENAME,
- // column name
- new String[] { PrivateDataContract.COLNAME_L2KEY },
- null, // selection
- null, // selectionArgs
- null, // groupBy
- null, // having
- null, // orderBy
- "1"); // limit
- if (0 != cursorPrivateData.getCount()) {
- cursorPrivateData.close();
- continue;
- }
- cursorPrivateData.close();
- db.setTransactionSuccessful();
- return;
- } catch (SQLiteException e) {
- Log.e(TAG, "Could not wipe the data in database", e);
- } finally {
- db.endTransaction();
- }
- }
- }
-
- /**
- * The following is a horrible hack that is necessary because the Android SQLite API does not
- * have a way to query a binary blob. This, almost certainly, is an overlook.
- *
- * The Android SQLite API has two family of methods : one for query that returns data, and
- * one for more general SQL statements that can execute any statement but may not return
- * anything. All the query methods, however, take only String[] for the arguments.
- *
- * In principle it is simple to write a function that will encode the binary blob in the
- * way SQLite expects it. However, because the API forces the argument to be coerced into a
- * String, the SQLiteQuery object generated by the default query methods will bind all
- * arguments as Strings and SQL will *sanitize* them. This works okay for numeric types,
- * but the format for blobs is x'<hex string>'. Note the presence of quotes, which will
- * be sanitized, changing the contents of the field, and the query will fail to match the
- * blob.
- *
- * As far as I can tell, there are two possible ways around this problem. The first one
- * is to put the data in the query string and eschew it being an argument. This would
- * require doing the sanitizing by hand. The other is to call bindBlob directly on the
- * generated SQLiteQuery object, which not only is a lot less dangerous than rolling out
- * sanitizing, but also will do the right thing if the underlying format ever changes.
- *
- * But none of the methods that take an SQLiteQuery object can return data ; this *must*
- * be called with SQLiteDatabase#query. This object is not accessible from outside.
- * However, there is a #query version that accepts a CursorFactory and this is pretty
- * straightforward to implement as all the arguments are coming in and the SQLiteCursor
- * class is public API.
- * With this, it's possible to intercept the SQLiteQuery object, and assuming the args
- * are available, to bind them directly and work around the API's oblivious coercion into
- * Strings.
- *
- * This is really sad, but I don't see another way of having this work than this or the
- * hand-rolled sanitizing, and this is the lesser evil.
- */
- private static class CustomCursorFactory implements SQLiteDatabase.CursorFactory {
- @NonNull
- private final ArrayList<Object> mArgs;
- CustomCursorFactory(@NonNull final ArrayList<Object> args) {
- mArgs = args;
- }
- @Override
- public Cursor newCursor(final SQLiteDatabase db, final SQLiteCursorDriver masterQuery,
- final String editTable,
- final SQLiteQuery query) {
- int index = 1; // bind is 1-indexed
- for (final Object arg : mArgs) {
- if (arg instanceof String) {
- query.bindString(index++, (String) arg);
- } else if (arg instanceof Long) {
- query.bindLong(index++, (Long) arg);
- } else if (arg instanceof Integer) {
- query.bindLong(index++, Long.valueOf((Integer) arg));
- } else if (arg instanceof byte[]) {
- query.bindBlob(index++, (byte[]) arg);
- } else {
- throw new IllegalStateException("Unsupported type CustomCursorFactory "
- + arg.getClass().toString());
- }
- }
- return new SQLiteCursor(masterQuery, editTable, query);
- }
- }
-
- // Returns the l2key of the closest match, if and only if it matches
- // closely enough (as determined by group-closeness).
- @Nullable
- static String findClosestAttributes(@NonNull final SQLiteDatabase db,
- @NonNull final NetworkAttributes attr) {
- if (attr.isEmpty()) return null;
- final ContentValues values = toContentValues(attr);
-
- // Build the selection and args. To cut down on the number of lines to search, limit
- // the search to those with at least one argument equals to the requested attributes.
- // This works only because null attributes match only will not result in group-closeness.
- final StringJoiner sj = new StringJoiner(" OR ");
- final ArrayList<Object> args = new ArrayList<>();
- args.add(System.currentTimeMillis());
- for (final String field : values.keySet()) {
- sj.add(field + " = ?");
- args.add(values.get(field));
- }
-
- final String selection = NetworkAttributesContract.COLNAME_EXPIRYDATE + " > ? AND ("
- + sj.toString() + ")";
- final Cursor cursor = db.queryWithFactory(new CustomCursorFactory(args),
- false, // distinct
- NetworkAttributesContract.TABLENAME,
- null, // columns, null means everything
- selection, // selection
- null, // selectionArgs, horrendously passed to the cursor factory instead
- null, // groupBy
- null, // having
- null, // orderBy
- null); // limit
- if (cursor.getCount() <= 0) return null;
- cursor.moveToFirst();
- String bestKey = null;
- float bestMatchConfidence = GROUPCLOSE_CONFIDENCE; // Never return a match worse than this.
- while (!cursor.isAfterLast()) {
- final NetworkAttributes read = readNetworkAttributesLine(cursor);
- final float confidence = read.getNetworkGroupSamenessConfidence(attr);
- if (confidence > bestMatchConfidence) {
- bestKey = getString(cursor, NetworkAttributesContract.COLNAME_L2KEY);
- bestMatchConfidence = confidence;
- }
- cursor.moveToNext();
- }
- cursor.close();
- return bestKey;
- }
-
- // Drops all records that are expired. Relevance has decayed to zero of these records. Returns
- // an int out of Status.{SUCCESS, ERROR_*}
- static int dropAllExpiredRecords(@NonNull final SQLiteDatabase db) {
- db.beginTransaction();
- try {
- // Deletes NetworkAttributes that have expired.
- db.delete(NetworkAttributesContract.TABLENAME,
- NetworkAttributesContract.COLNAME_EXPIRYDATE + " < ?",
- new String[]{Long.toString(System.currentTimeMillis())});
- db.setTransactionSuccessful();
- } catch (SQLiteException e) {
- Log.e(TAG, "Could not delete data from memory store", e);
- return Status.ERROR_STORAGE;
- } finally {
- db.endTransaction();
- }
-
- // Execute vacuuming here if above operation has no exception. If above operation got
- // exception, vacuuming can be ignored for reducing unnecessary consumption.
- try {
- db.execSQL("VACUUM");
- } catch (SQLiteException e) {
- // Do nothing.
- }
- return Status.SUCCESS;
- }
-
- // Drops number of records that start from the lowest expiryDate. Returns an int out of
- // Status.{SUCCESS, ERROR_*}
- static int dropNumberOfRecords(@NonNull final SQLiteDatabase db, int number) {
- if (number <= 0) {
- return Status.ERROR_ILLEGAL_ARGUMENT;
- }
-
- // Queries number of NetworkAttributes that start from the lowest expiryDate.
- final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME,
- new String[] {NetworkAttributesContract.COLNAME_EXPIRYDATE}, // columns
- null, // selection
- null, // selectionArgs
- null, // groupBy
- null, // having
- NetworkAttributesContract.COLNAME_EXPIRYDATE, // orderBy
- Integer.toString(number)); // limit
- if (cursor == null || cursor.getCount() <= 0) return Status.ERROR_GENERIC;
- cursor.moveToLast();
-
- //Get the expiryDate from last record.
- final long expiryDate = getLong(cursor, NetworkAttributesContract.COLNAME_EXPIRYDATE, 0);
- cursor.close();
-
- db.beginTransaction();
- try {
- // Deletes NetworkAttributes that expiryDate are lower than given value.
- db.delete(NetworkAttributesContract.TABLENAME,
- NetworkAttributesContract.COLNAME_EXPIRYDATE + " <= ?",
- new String[]{Long.toString(expiryDate)});
- db.setTransactionSuccessful();
- } catch (SQLiteException e) {
- Log.e(TAG, "Could not delete data from memory store", e);
- return Status.ERROR_STORAGE;
- } finally {
- db.endTransaction();
- }
-
- // Execute vacuuming here if above operation has no exception. If above operation got
- // exception, vacuuming can be ignored for reducing unnecessary consumption.
- try {
- db.execSQL("VACUUM");
- } catch (SQLiteException e) {
- // Do nothing.
- }
- return Status.SUCCESS;
- }
-
- static int getTotalRecordNumber(@NonNull final SQLiteDatabase db) {
- // Query the total number of NetworkAttributes
- final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME,
- new String[] {"COUNT(*)"}, // columns
- null, // selection
- null, // selectionArgs
- null, // groupBy
- null, // having
- null); // orderBy
- cursor.moveToFirst();
- return cursor == null ? 0 : cursor.getInt(0);
- }
-
- // Helper methods
- private static String getString(final Cursor cursor, final String columnName) {
- final int columnIndex = cursor.getColumnIndex(columnName);
- return (columnIndex >= 0) ? cursor.getString(columnIndex) : null;
- }
- private static byte[] getBlob(final Cursor cursor, final String columnName) {
- final int columnIndex = cursor.getColumnIndex(columnName);
- return (columnIndex >= 0) ? cursor.getBlob(columnIndex) : null;
- }
- private static int getInt(final Cursor cursor, final String columnName,
- final int defaultValue) {
- final int columnIndex = cursor.getColumnIndex(columnName);
- return (columnIndex >= 0) ? cursor.getInt(columnIndex) : defaultValue;
- }
- private static long getLong(final Cursor cursor, final String columnName,
- final long defaultValue) {
- final int columnIndex = cursor.getColumnIndex(columnName);
- return (columnIndex >= 0) ? cursor.getLong(columnIndex) : defaultValue;
- }
-}
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java
deleted file mode 100644
index 55ab8d4..0000000
--- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java
+++ /dev/null
@@ -1,503 +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.server.connectivity.ipmemorystore;
-
-import static android.net.ipmemorystore.Status.ERROR_DATABASE_CANNOT_BE_OPENED;
-import static android.net.ipmemorystore.Status.ERROR_GENERIC;
-import static android.net.ipmemorystore.Status.ERROR_ILLEGAL_ARGUMENT;
-import static android.net.ipmemorystore.Status.SUCCESS;
-
-import static com.android.server.connectivity.ipmemorystore.IpMemoryStoreDatabase.EXPIRY_ERROR;
-import static com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService.InterruptMaintenance;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.database.SQLException;
-import android.database.sqlite.SQLiteDatabase;
-import android.net.IIpMemoryStore;
-import android.net.ipmemorystore.Blob;
-import android.net.ipmemorystore.IOnBlobRetrievedListener;
-import android.net.ipmemorystore.IOnL2KeyResponseListener;
-import android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener;
-import android.net.ipmemorystore.IOnSameL3NetworkResponseListener;
-import android.net.ipmemorystore.IOnStatusListener;
-import android.net.ipmemorystore.NetworkAttributes;
-import android.net.ipmemorystore.NetworkAttributesParcelable;
-import android.net.ipmemorystore.SameL3NetworkResponse;
-import android.net.ipmemorystore.Status;
-import android.net.ipmemorystore.StatusParcelable;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.File;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/**
- * Implementation for the IP memory store.
- * This component offers specialized services for network components to store and retrieve
- * knowledge about networks, and provides intelligence that groups level 2 networks together
- * into level 3 networks.
- *
- * @hide
- */
-public class IpMemoryStoreService extends IIpMemoryStore.Stub {
- private static final String TAG = IpMemoryStoreService.class.getSimpleName();
- private static final int DATABASE_SIZE_THRESHOLD = 10 * 1024 * 1024; //10MB
- private static final int MAX_DROP_RECORD_TIMES = 500;
- private static final int MIN_DELETE_NUM = 5;
- private static final boolean DBG = true;
-
- // Error codes below are internal and used for notifying status beteween IpMemoryStore modules.
- static final int ERROR_INTERNAL_BASE = -1_000_000_000;
- // This error code is used for maintenance only to notify RegularMaintenanceJobService that
- // full maintenance job has been interrupted.
- static final int ERROR_INTERNAL_INTERRUPTED = ERROR_INTERNAL_BASE - 1;
-
- @NonNull
- final Context mContext;
- @Nullable
- final SQLiteDatabase mDb;
- @NonNull
- final ExecutorService mExecutor;
-
- /**
- * Construct an IpMemoryStoreService object.
- * This constructor will block on disk access to open the database.
- * @param context the context to access storage with.
- */
- public IpMemoryStoreService(@NonNull final Context context) {
- // Note that constructing the service will access the disk and block
- // for some time, but it should make no difference to the clients. Because
- // the interface is one-way, clients fire and forget requests, and the callback
- // will get called eventually in any case, and the framework will wait for the
- // service to be created to deliver subsequent requests.
- // Avoiding this would mean the mDb member can't be final, which means the service would
- // have to test for nullity, care for failure, and allow for a wait at every single access,
- // which would make the code a lot more complex and require all methods to possibly block.
- mContext = context;
- SQLiteDatabase db;
- final IpMemoryStoreDatabase.DbHelper helper = new IpMemoryStoreDatabase.DbHelper(context);
- try {
- db = helper.getWritableDatabase();
- if (null == db) Log.e(TAG, "Unexpected null return of getWriteableDatabase");
- } catch (final SQLException e) {
- Log.e(TAG, "Can't open the Ip Memory Store database", e);
- db = null;
- } catch (final Exception e) {
- Log.wtf(TAG, "Impossible exception Ip Memory Store database", e);
- db = null;
- }
- mDb = db;
- // The single thread executor guarantees that all work is executed sequentially on the
- // same thread, and no two tasks can be active at the same time. This is required to
- // ensure operations from multiple clients don't interfere with each other (in particular,
- // operations involving a transaction must not run concurrently with other operations
- // as the other operations might be taken as part of the transaction). By default, the
- // single thread executor runs off an unbounded queue.
- // TODO : investigate replacing this scheme with a scheme where each thread has its own
- // instance of the database, as it may be faster. It is likely however that IpMemoryStore
- // operations are mostly IO-bound anyway, and additional contention is unlikely to bring
- // benefits. Alternatively, a read-write lock might increase throughput.
- mExecutor = Executors.newSingleThreadExecutor();
- RegularMaintenanceJobService.schedule(mContext, this);
- }
-
- /**
- * Shutdown the memory store service, cancelling running tasks and dropping queued tasks.
- *
- * This is provided to give a way to clean up, and is meant to be available in case of an
- * emergency shutdown.
- */
- public void shutdown() {
- // By contrast with ExecutorService#shutdown, ExecutorService#shutdownNow tries
- // to cancel the existing tasks, and does not wait for completion. It does not
- // guarantee the threads can be terminated in any given amount of time.
- mExecutor.shutdownNow();
- if (mDb != null) mDb.close();
- RegularMaintenanceJobService.unschedule(mContext);
- }
-
- /** Helper function to make a status object */
- private StatusParcelable makeStatus(final int code) {
- return new Status(code).toParcelable();
- }
-
- /**
- * Store network attributes for a given L2 key.
- *
- * @param l2Key The L2 key for the L2 network. Clients that don't know or care about the L2
- * key and only care about grouping can pass a unique ID here like the ones
- * generated by {@code java.util.UUID.randomUUID()}, but keep in mind the low
- * relevance of such a network will lead to it being evicted soon if it's not
- * refreshed. Use findL2Key to try and find a similar L2Key to these attributes.
- * @param attributes The attributes for this network.
- * @param listener A listener to inform of the completion of this call, or null if the client
- * is not interested in learning about success/failure.
- * Through the listener, returns the L2 key. This is useful if the L2 key was not specified.
- * If the call failed, the L2 key will be null.
- */
- // Note that while l2Key and attributes are non-null in spirit, they are received from
- // another process. If the remote process decides to ignore everything and send null, this
- // process should still not crash.
- @Override
- public void storeNetworkAttributes(@Nullable final String l2Key,
- @Nullable final NetworkAttributesParcelable attributes,
- @Nullable final IOnStatusListener listener) {
- // Because the parcelable is 100% mutable, the thread may not see its members initialized.
- // Therefore either an immutable object is created on this same thread before it's passed
- // to the executor, or there need to be a write barrier here and a read barrier in the
- // remote thread.
- final NetworkAttributes na = null == attributes ? null : new NetworkAttributes(attributes);
- mExecutor.execute(() -> {
- try {
- final int code = storeNetworkAttributesAndBlobSync(l2Key, na,
- null /* clientId */, null /* name */, null /* data */);
- if (null != listener) listener.onComplete(makeStatus(code));
- } catch (final RemoteException e) {
- // Client at the other end died
- }
- });
- }
-
- /**
- * Store a binary blob associated with an L2 key and a name.
- *
- * @param l2Key The L2 key for this network.
- * @param clientId The ID of the client.
- * @param name The name of this data.
- * @param blob The data to store.
- * @param listener The listener that will be invoked to return the answer, or null if the
- * is not interested in learning about success/failure.
- * Through the listener, returns a status to indicate success or failure.
- */
- @Override
- public void storeBlob(@Nullable final String l2Key, @Nullable final String clientId,
- @Nullable final String name, @Nullable final Blob blob,
- @Nullable final IOnStatusListener listener) {
- final byte[] data = null == blob ? null : blob.data;
- mExecutor.execute(() -> {
- try {
- final int code = storeNetworkAttributesAndBlobSync(l2Key,
- null /* NetworkAttributes */, clientId, name, data);
- if (null != listener) listener.onComplete(makeStatus(code));
- } catch (final RemoteException e) {
- // Client at the other end died
- }
- });
- }
-
- /**
- * Helper method for storeNetworkAttributes and storeBlob.
- *
- * Either attributes or none of clientId, name and data may be null. This will write the
- * passed data if non-null, and will write attributes if non-null, but in any case it will
- * bump the relevance up.
- * Returns a success code from Status.
- */
- private int storeNetworkAttributesAndBlobSync(@Nullable final String l2Key,
- @Nullable final NetworkAttributes attributes,
- @Nullable final String clientId,
- @Nullable final String name, @Nullable final byte[] data) {
- if (null == l2Key) return ERROR_ILLEGAL_ARGUMENT;
- if (null == attributes && null == data) return ERROR_ILLEGAL_ARGUMENT;
- if (null != data && (null == clientId || null == name)) return ERROR_ILLEGAL_ARGUMENT;
- if (null == mDb) return ERROR_DATABASE_CANNOT_BE_OPENED;
- try {
- final long oldExpiry = IpMemoryStoreDatabase.getExpiry(mDb, l2Key);
- final long newExpiry = RelevanceUtils.bumpExpiryDate(
- oldExpiry == EXPIRY_ERROR ? System.currentTimeMillis() : oldExpiry);
- final int errorCode =
- IpMemoryStoreDatabase.storeNetworkAttributes(mDb, l2Key, newExpiry, attributes);
- // If no blob to store, the client is interested in the result of storing the attributes
- if (null == data) return errorCode;
- // Otherwise it's interested in the result of storing the blob
- return IpMemoryStoreDatabase.storeBlob(mDb, l2Key, clientId, name, data);
- } catch (Exception e) {
- if (DBG) {
- Log.e(TAG, "Exception while storing for key {" + l2Key
- + "} ; NetworkAttributes {" + (null == attributes ? "null" : attributes)
- + "} ; clientId {" + (null == clientId ? "null" : clientId)
- + "} ; name {" + (null == name ? "null" : name)
- + "} ; data {" + Utils.byteArrayToString(data) + "}", e);
- }
- }
- return ERROR_GENERIC;
- }
-
- /**
- * Returns the best L2 key associated with the attributes.
- *
- * This will find a record that would be in the same group as the passed attributes. This is
- * useful to choose the key for storing a sample or private data when the L2 key is not known.
- * If multiple records are group-close to these attributes, the closest match is returned.
- * If multiple records have the same closeness, the one with the smaller (unicode codepoint
- * order) L2 key is returned.
- * If no record matches these attributes, null is returned.
- *
- * @param attributes The attributes of the network to find.
- * @param listener The listener that will be invoked to return the answer.
- * Through the listener, returns the L2 key if one matched, or null.
- */
- @Override
- public void findL2Key(@Nullable final NetworkAttributesParcelable attributes,
- @Nullable final IOnL2KeyResponseListener listener) {
- if (null == listener) return;
- mExecutor.execute(() -> {
- try {
- if (null == attributes) {
- listener.onL2KeyResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null);
- return;
- }
- if (null == mDb) {
- listener.onL2KeyResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null);
- return;
- }
- final String key = IpMemoryStoreDatabase.findClosestAttributes(mDb,
- new NetworkAttributes(attributes));
- listener.onL2KeyResponse(makeStatus(SUCCESS), key);
- } catch (final RemoteException e) {
- // Client at the other end died
- }
- });
- }
-
- /**
- * Returns whether, to the best of the store's ability to tell, the two specified L2 keys point
- * to the same L3 network. Group-closeness is used to determine this.
- *
- * @param l2Key1 The key for the first network.
- * @param l2Key2 The key for the second network.
- * @param listener The listener that will be invoked to return the answer.
- * Through the listener, a SameL3NetworkResponse containing the answer and confidence.
- */
- @Override
- public void isSameNetwork(@Nullable final String l2Key1, @Nullable final String l2Key2,
- @Nullable final IOnSameL3NetworkResponseListener listener) {
- if (null == listener) return;
- mExecutor.execute(() -> {
- try {
- if (null == l2Key1 || null == l2Key2) {
- listener.onSameL3NetworkResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null);
- return;
- }
- if (null == mDb) {
- listener.onSameL3NetworkResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null);
- return;
- }
- try {
- final NetworkAttributes attr1 =
- IpMemoryStoreDatabase.retrieveNetworkAttributes(mDb, l2Key1);
- final NetworkAttributes attr2 =
- IpMemoryStoreDatabase.retrieveNetworkAttributes(mDb, l2Key2);
- if (null == attr1 || null == attr2) {
- listener.onSameL3NetworkResponse(makeStatus(SUCCESS),
- new SameL3NetworkResponse(l2Key1, l2Key2,
- -1f /* never connected */).toParcelable());
- return;
- }
- final float confidence = attr1.getNetworkGroupSamenessConfidence(attr2);
- listener.onSameL3NetworkResponse(makeStatus(SUCCESS),
- new SameL3NetworkResponse(l2Key1, l2Key2, confidence).toParcelable());
- } catch (Exception e) {
- listener.onSameL3NetworkResponse(makeStatus(ERROR_GENERIC), null);
- }
- } catch (final RemoteException e) {
- // Client at the other end died
- }
- });
- }
-
- /**
- * Retrieve the network attributes for a key.
- * If no record is present for this key, this will return null attributes.
- *
- * @param l2Key The key of the network to query.
- * @param listener The listener that will be invoked to return the answer.
- * Through the listener, returns the network attributes and the L2 key associated with
- * the query.
- */
- @Override
- public void retrieveNetworkAttributes(@Nullable final String l2Key,
- @Nullable final IOnNetworkAttributesRetrievedListener listener) {
- if (null == listener) return;
- mExecutor.execute(() -> {
- try {
- if (null == l2Key) {
- listener.onNetworkAttributesRetrieved(
- makeStatus(ERROR_ILLEGAL_ARGUMENT), l2Key, null);
- return;
- }
- if (null == mDb) {
- listener.onNetworkAttributesRetrieved(
- makeStatus(ERROR_DATABASE_CANNOT_BE_OPENED), l2Key, null);
- return;
- }
- try {
- final NetworkAttributes attributes =
- IpMemoryStoreDatabase.retrieveNetworkAttributes(mDb, l2Key);
- listener.onNetworkAttributesRetrieved(makeStatus(SUCCESS), l2Key,
- null == attributes ? null : attributes.toParcelable());
- } catch (final Exception e) {
- listener.onNetworkAttributesRetrieved(makeStatus(ERROR_GENERIC), l2Key, null);
- }
- } catch (final RemoteException e) {
- // Client at the other end died
- }
- });
- }
-
- /**
- * Retrieve previously stored private data.
- * If no data was stored for this L2 key and name this will return null.
- *
- * @param l2Key The L2 key.
- * @param clientId The id of the client that stored this data.
- * @param name The name of the data.
- * @param listener The listener that will be invoked to return the answer.
- * Through the listener, returns the private data if any or null if none, with the L2 key
- * and the name of the data associated with the query.
- */
- @Override
- public void retrieveBlob(@NonNull final String l2Key, @NonNull final String clientId,
- @NonNull final String name, @NonNull final IOnBlobRetrievedListener listener) {
- if (null == listener) return;
- mExecutor.execute(() -> {
- try {
- if (null == l2Key) {
- listener.onBlobRetrieved(makeStatus(ERROR_ILLEGAL_ARGUMENT), l2Key, name, null);
- return;
- }
- if (null == mDb) {
- listener.onBlobRetrieved(makeStatus(ERROR_DATABASE_CANNOT_BE_OPENED), l2Key,
- name, null);
- return;
- }
- try {
- final Blob b = new Blob();
- b.data = IpMemoryStoreDatabase.retrieveBlob(mDb, l2Key, clientId, name);
- listener.onBlobRetrieved(makeStatus(SUCCESS), l2Key, name, b);
- } catch (final Exception e) {
- listener.onBlobRetrieved(makeStatus(ERROR_GENERIC), l2Key, name, null);
- }
- } catch (final RemoteException e) {
- // Client at the other end died
- }
- });
- }
-
- /**
- * Wipe the data in IpMemoryStore database upon network factory reset.
- */
- @Override
- public void factoryReset() {
- mExecutor.execute(() -> IpMemoryStoreDatabase.wipeDataUponNetworkReset(mDb));
- }
-
- /** Get db size threshold. */
- @VisibleForTesting
- protected int getDbSizeThreshold() {
- return DATABASE_SIZE_THRESHOLD;
- }
-
- private long getDbSize() {
- final File dbFile = new File(mDb.getPath());
- try {
- return dbFile.length();
- } catch (final SecurityException e) {
- if (DBG) Log.e(TAG, "Read db size access deny.", e);
- // Return zero value if can't get disk usage exactly.
- return 0;
- }
- }
-
- /** Check if db size is over the threshold. */
- @VisibleForTesting
- boolean isDbSizeOverThreshold() {
- return getDbSize() > getDbSizeThreshold();
- }
-
- /**
- * Full maintenance.
- *
- * @param listener A listener to inform of the completion of this call.
- */
- void fullMaintenance(@NonNull final IOnStatusListener listener,
- @NonNull final InterruptMaintenance interrupt) {
- mExecutor.execute(() -> {
- try {
- if (null == mDb) {
- listener.onComplete(makeStatus(ERROR_DATABASE_CANNOT_BE_OPENED));
- return;
- }
-
- // Interrupt maintenance because the scheduling job has been canceled.
- if (checkForInterrupt(listener, interrupt)) return;
-
- int result = SUCCESS;
- // Drop all records whose relevance has decayed to zero.
- // This is the first step to decrease memory store size.
- result = IpMemoryStoreDatabase.dropAllExpiredRecords(mDb);
-
- if (checkForInterrupt(listener, interrupt)) return;
-
- // Aggregate historical data in passes
- // TODO : Waiting for historical data implement.
-
- // Check if db size meets the storage goal(10MB). If not, keep dropping records and
- // aggregate historical data until the storage goal is met. Use for loop with 500
- // times restriction to prevent infinite loop (Deleting records always fail and db
- // size is still over the threshold)
- for (int i = 0; isDbSizeOverThreshold() && i < MAX_DROP_RECORD_TIMES; i++) {
- if (checkForInterrupt(listener, interrupt)) return;
-
- final int totalNumber = IpMemoryStoreDatabase.getTotalRecordNumber(mDb);
- final long dbSize = getDbSize();
- final float decreaseRate = (dbSize == 0)
- ? 0 : (float) (dbSize - getDbSizeThreshold()) / (float) dbSize;
- final int deleteNumber = Math.max(
- (int) (totalNumber * decreaseRate), MIN_DELETE_NUM);
-
- result = IpMemoryStoreDatabase.dropNumberOfRecords(mDb, deleteNumber);
-
- if (checkForInterrupt(listener, interrupt)) return;
-
- // Aggregate historical data
- // TODO : Waiting for historical data implement.
- }
- listener.onComplete(makeStatus(result));
- } catch (final RemoteException e) {
- // Client at the other end died
- }
- });
- }
-
- private boolean checkForInterrupt(@NonNull final IOnStatusListener listener,
- @NonNull final InterruptMaintenance interrupt) throws RemoteException {
- if (!interrupt.isInterrupted()) return false;
- listener.onComplete(makeStatus(ERROR_INTERNAL_INTERRUPTED));
- return true;
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
-}
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java
deleted file mode 100644
index bea7052..0000000
--- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.connectivity.ipmemorystore;
-
-import android.app.job.JobInfo;
-import android.app.job.JobParameters;
-import android.app.job.JobScheduler;
-import android.app.job.JobService;
-import android.content.ComponentName;
-import android.content.Context;
-import android.net.ipmemorystore.IOnStatusListener;
-import android.net.ipmemorystore.Status;
-import android.net.ipmemorystore.StatusParcelable;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Regular maintenance job service.
- * @hide
- */
-public final class RegularMaintenanceJobService extends JobService {
- // Must be unique within the system server uid.
- public static final int REGULAR_MAINTENANCE_ID = 3345678;
-
- /**
- * Class for interrupt check of maintenance job.
- */
- public static final class InterruptMaintenance {
- private volatile boolean mIsInterrupted;
- private final int mJobId;
-
- public InterruptMaintenance(int jobId) {
- mJobId = jobId;
- mIsInterrupted = false;
- }
-
- public int getJobId() {
- return mJobId;
- }
-
- public void setInterrupted(boolean interrupt) {
- mIsInterrupted = interrupt;
- }
-
- public boolean isInterrupted() {
- return mIsInterrupted;
- }
- }
-
- private static final ArrayList<InterruptMaintenance> sInterruptList = new ArrayList<>();
- private static IpMemoryStoreService sIpMemoryStoreService;
-
- @Override
- public boolean onStartJob(JobParameters params) {
- if (sIpMemoryStoreService == null) {
- Log.wtf("RegularMaintenanceJobService",
- "Can not start job because sIpMemoryStoreService is null.");
- return false;
- }
- final InterruptMaintenance im = new InterruptMaintenance(params.getJobId());
- sInterruptList.add(im);
-
- sIpMemoryStoreService.fullMaintenance(new IOnStatusListener() {
- @Override
- public void onComplete(final StatusParcelable statusParcelable) throws RemoteException {
- final Status result = new Status(statusParcelable);
- if (!result.isSuccess()) {
- Log.e("RegularMaintenanceJobService", "Regular maintenance failed."
- + " Error is " + result.resultCode);
- }
- sInterruptList.remove(im);
- jobFinished(params, !result.isSuccess());
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
-
- @Override
- public IBinder asBinder() {
- return null;
- }
- }, im);
- return true;
- }
-
- @Override
- public boolean onStopJob(JobParameters params) {
- final int jobId = params.getJobId();
- for (InterruptMaintenance im : sInterruptList) {
- if (im.getJobId() == jobId) {
- im.setInterrupted(true);
- }
- }
- return true;
- }
-
- /** Schedule regular maintenance job */
- static void schedule(Context context, IpMemoryStoreService ipMemoryStoreService) {
- final JobScheduler jobScheduler =
- (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
-
- final ComponentName maintenanceJobName =
- new ComponentName(context, RegularMaintenanceJobService.class);
-
- // Regular maintenance is scheduled for when the device is idle with access power and a
- // minimum interval of one day.
- final JobInfo regularMaintenanceJob =
- new JobInfo.Builder(REGULAR_MAINTENANCE_ID, maintenanceJobName)
- .setRequiresDeviceIdle(true)
- .setRequiresCharging(true)
- .setRequiresBatteryNotLow(true)
- .setPeriodic(TimeUnit.HOURS.toMillis(24)).build();
-
- jobScheduler.schedule(regularMaintenanceJob);
- sIpMemoryStoreService = ipMemoryStoreService;
- }
-
- /** Unschedule regular maintenance job */
- static void unschedule(Context context) {
- final JobScheduler jobScheduler =
- (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
- jobScheduler.cancel(REGULAR_MAINTENANCE_ID);
- sIpMemoryStoreService = null;
- }
-}
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RelevanceUtils.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RelevanceUtils.java
deleted file mode 100644
index 38d5544..0000000
--- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RelevanceUtils.java
+++ /dev/null
@@ -1,307 +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.server.connectivity.ipmemorystore;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * A class containing the logic around the relevance value for
- * IP Memory Store.
- *
- * @hide
- */
-public class RelevanceUtils {
- /**
- * The relevance is a decaying value that gets lower and lower until it
- * reaches 0 after some time passes. It follows an exponential decay law,
- * dropping slowly at first then faster and faster, because a network is
- * likely to be visited again if it was visited not long ago, and the longer
- * it hasn't been visited the more likely it is that it won't be visited
- * again. For example, a network visited on holiday should stay fresh for
- * the duration of the holiday and persist for a while, but after the venue
- * hasn't been visited for a while it should quickly be discarded. What
- * should accelerate forgetting the network is extended periods without
- * visits, so that occasional venues get discarded but regular visits keep
- * the network relevant, even if the visits are infrequent.
- *
- * This function must be stable by iteration, meaning that adjusting the same value
- * for different dates iteratively multiple times should give the same result.
- * Formally, if f is the decay function that associates a relevance x at a date d1
- * to the value at ulterior date d3, then for any date d2 between d1 and d3 :
- * f(x, d3 - d1) = f(f(x, d3 - d2), d2 - d1). Intuitively, this property simply
- * means it should be the same to compute and store back the value after two months,
- * or to do it once after one month, store it back, and do it again after another
- * months has passed.
- * The pair of the relevance and date define the entire curve, so any pair
- * of values on the curve will define the same curve. Setting one of them to a
- * constant, so as not to have to store it, means the other one will always suffice
- * to describe the curve. For example, only storing the date for a known, constant
- * value of the relevance is an efficient way of remembering this information (and
- * to compare relevances together, as f is monotonically decreasing).
- *
- *** Choosing the function :
- * Functions of the kind described above are standard exponential decay functions
- * like the ones that govern atomic decay where the value at any given date can be
- * computed uniformly from the value at a previous date and the time elapsed since
- * that date. It is simple to picture this kind of function as one where after a
- * given period of time called the half-life, the relevance value will have been
- * halved. Decay of this kind is expressed in function of the previous value by
- * functions like
- * f(x, t) = x * F ^ (t / L)
- * ...where x is the value, t is the elapsed time, L is the half-life (or more
- * generally the F-th-life) and F the decay factor (typically 0.5, hence why L is
- * usually called the half-life). The ^ symbol here is used for exponentiation.
- * Or, starting at a given M for t = 0 :
- * f(t) = M * F ^ (t / L)
- *
- * Because a line in the store needs to become irrelevant at some point but
- * this class of functions never go to 0, a minimum cutoff has to be chosen to
- * represent irrelevance. The simpler way of doing this is to simply add this
- * minimum cutoff to the computation before and removing it after.
- * Thus the function becomes :
- * f(x, t) = ((x + K) * F ^ (t / L)) - K
- * ...where K is the minimum cutoff, L the half-life, and F the factor between
- * the original x and x after its half-life. Strictly speaking using the word
- * "half-life" implies that F = 0.5, but the relation works for any value of F.
- *
- * It is easy enough to check that this function satisfies the stability
- * relation that was given above for any value of F, L and K, which become
- * parameters that can be defined at will.
- *
- * relevance
- * 1.0 |
- * |\
- * | \
- * | \ (this graph rendered with L = 75 days and K = 1/40)
- * 0.75| ',
- * | \
- * | '.
- * | \.
- * | \
- * 0.5 | '\
- * | ''.
- * | ''.
- * | ''.
- * 0.25| '''..
- * | '''..
- * | ''''....
- * | '''''..........
- * 0 +-------------------------------------------------------''''''''''----
- * 0 50 100 150 200 250 300 350 400 days
- *
- *** Choosing the parameters
- * The maximum M is an arbitrary parameter that simply scales the curve.
- * The tradeoff for M is pretty simple : if the relevance is going to be an
- * integer, the bigger M is the more precision there is in the relevance.
- * However, values of M that are easy for humans to read are preferable to
- * help debugging, and a suitably low value may be enough to ensure there
- * won't be integer overflows in intermediate computations.
- * A value of 1_000_000 probably is plenty for precision, while still in the
- * low range of what ints can represent.
- *
- * F and L are parameters to be chosen arbitrarily and have an impact on how
- * fast the relevance will be decaying at first, keeping in mind that
- * the 400 days value and the cap stay the same. In simpler words, F and L
- * define the steepness of the curve.
- * To keep things simple (and familiar) F is arbitrarily chosen to be 0.5, and
- * L is set to 200 days visually to achieve the desired effect. Refer to the
- * illustration above to get a feel of how that feels.
- *
- * Moreover, the memory store works on an assumption that the relevance should
- * be capped, and that an entry with capped relevance should decay in 400 days.
- * This is on premises that the networks a device will need to remember the
- * longest should be networks visited about once a year.
- * For this reason, the relevance is at the maximum M 400 days before expiry :
- * f(M, 400 days) = 0
- * From replacing this with the value of the function, K can then be derived
- * from the values of M, F and L :
- * (M + K) * F ^ (t / L) - K = 0
- * K = M * F ^ (400 days / L) / (1 - F ^ (400 days / L))
- * Replacing with actual values this gives :
- * K = 1_000_000 * 0.5 ^ (400 / 200) / (1 - 0.5 ^ (400 / 200))
- * = 1_000_000 / 3 ≈ 333_333.3
- * This ensures the function has the desired profile, the desired value at
- * cap, and the desired value at expiry.
- *
- *** Useful relations
- * Let's define the expiry time for any given relevance x as the interval of
- * time such as :
- * f(x, expiry) = 0
- * which can be rewritten
- * ((x + K) * F ^ (expiry / L)) = K
- * ...giving an expression of the expiry in function of the relevance x as
- * expiry = L * logF(K / (x + K))
- * Conversely the relevance x can be expressed in function of the expiry as
- * x = K / F ^ (expiry / L) - K
- * These relations are useful in utility functions.
- *
- *** Bumping things up
- * The last issue therefore is to decide how to bump up the relevance. The
- * simple approach is to simply lift up the curve a little bit by a constant
- * normalized amount, delaying the time of expiry. For example increasing
- * the relevance by an amount I gives :
- * x2 = x1 + I
- * x2 and x1 correspond to two different expiry times expiry2 and expiry1,
- * and replacing x1 and x2 in the relation above with their expression in
- * function of the expiry comes :
- * K / F ^ (expiry2 / L) - K = K / F ^ (expiry1 / L) - K + I
- * which resolves to :
- * expiry2 = L * logF(K / (I + K / F ^ (expiry1 / L)))
- *
- * In this implementation, the bump is defined as 1/25th of the cap for
- * the relevance. This means a network will be remembered for the maximum
- * period of 400 days if connected 25 times in succession not accounting
- * for decay. Of course decay actually happens so it will take more than 25
- * connections for any given network to actually reach the cap, but because
- * decay is slow at first, it is a good estimate of how fast cap happens.
- *
- * Specifically, it gives the following four results :
- * - A network that a device connects to once hits irrelevance about 32.7 days after
- * it was first registered if never connected again.
- * - A network that a device connects to once a day at a fixed hour will hit the cap
- * on the 27th connection.
- * - A network that a device connects to once a week at a fixed hour will hit the cap
- * on the 57th connection.
- * - A network that a device connects to every day for 7 straight days then never again
- * expires 144 days after the last connection.
- * These metrics tend to match pretty well the requirements.
- */
-
- // TODO : make these constants configurable at runtime. Don't forget to build it so that
- // changes will wipe the database, migrate the values, or otherwise make sure the relevance
- // values are still meaningful.
-
- // How long, in milliseconds, is a capped relevance valid for, or in other
- // words how many milliseconds after its relevance was set to RELEVANCE_CAP does
- // any given line expire. 400 days.
- @VisibleForTesting
- public static final long CAPPED_RELEVANCE_LIFETIME_MS = 400L * 24 * 60 * 60 * 1000;
-
- // The constant that represents a normalized 1.0 value for the relevance. In other words,
- // the cap for the relevance. This is referred to as M in the explanation above.
- @VisibleForTesting
- public static final int CAPPED_RELEVANCE = 1_000_000;
-
- // The decay factor. After a half-life, the relevance will have decayed by this value.
- // This is referred to as F in the explanation above.
- private static final double DECAY_FACTOR = 0.5;
-
- // The half-life. After this time, the relevance will have decayed by a factor DECAY_FACTOR.
- // This is referred to as L in the explanation above.
- private static final long HALF_LIFE_MS = 200L * 24 * 60 * 60 * 1000;
-
- // The value of the frame change. This is referred to as K in the explanation above.
- private static final double IRRELEVANCE_FLOOR =
- CAPPED_RELEVANCE * powF((double) CAPPED_RELEVANCE_LIFETIME_MS / HALF_LIFE_MS)
- / (1 - powF((double) CAPPED_RELEVANCE_LIFETIME_MS / HALF_LIFE_MS));
-
- // How much to bump the relevance by every time a line is written to.
- @VisibleForTesting
- public static final int RELEVANCE_BUMP = CAPPED_RELEVANCE / 25;
-
- // Java doesn't include a function for the logarithm in an arbitrary base, so implement it
- private static final double LOG_DECAY_FACTOR = Math.log(DECAY_FACTOR);
- private static double logF(final double value) {
- return Math.log(value) / LOG_DECAY_FACTOR;
- }
-
- // Utility function to get a power of the decay factor, to simplify the code.
- private static double powF(final double value) {
- return Math.pow(DECAY_FACTOR, value);
- }
-
- /**
- * Compute the value of the relevance now given an expiry date.
- *
- * @param expiry the date at which the column in the database expires.
- * @return the adjusted value of the relevance for this moment in time.
- */
- public static int computeRelevanceForNow(final long expiry) {
- return computeRelevanceForTargetDate(expiry, System.currentTimeMillis());
- }
-
- /**
- * Compute the value of the relevance at a given date from an expiry date.
- *
- * Because relevance decays with time, a relevance in the past corresponds to
- * a different relevance later.
- *
- * Relevance is always a positive value. 0 means not relevant at all.
- *
- * See the explanation at the top of this file to get the justification for this
- * computation.
- *
- * @param expiry the date at which the column in the database expires.
- * @param target the target date to adjust the relevance to.
- * @return the adjusted value of the relevance for the target moment.
- */
- public static int computeRelevanceForTargetDate(final long expiry, final long target) {
- final long delay = expiry - target;
- if (delay >= CAPPED_RELEVANCE_LIFETIME_MS) return CAPPED_RELEVANCE;
- if (delay <= 0) return 0;
- return (int) (IRRELEVANCE_FLOOR / powF((float) delay / HALF_LIFE_MS) - IRRELEVANCE_FLOOR);
- }
-
- /**
- * Compute the expiry duration adjusted up for a new fresh write.
- *
- * Every time data is written to the memory store for a given line, the
- * relevance is bumped up by a certain amount, which will boost the priority
- * of this line for computation of group attributes, and delay (possibly
- * indefinitely, if the line is accessed regularly) forgetting the data stored
- * in that line.
- * As opposed to bumpExpiryDate, this function uses a duration from now to expiry.
- *
- * See the explanation at the top of this file for a justification of this computation.
- *
- * @param oldExpiryDuration the old expiry duration in milliseconds from now.
- * @return the expiry duration representing a bumped up relevance value.
- */
- public static long bumpExpiryDuration(final long oldExpiryDuration) {
- // L * logF(K / (I + K / F ^ (expiry1 / L))), as documented above
- final double divisionFactor = powF(((double) oldExpiryDuration) / HALF_LIFE_MS);
- final double oldRelevance = IRRELEVANCE_FLOOR / divisionFactor;
- final long newDuration =
- (long) (HALF_LIFE_MS * logF(IRRELEVANCE_FLOOR / (RELEVANCE_BUMP + oldRelevance)));
- return Math.min(newDuration, CAPPED_RELEVANCE_LIFETIME_MS);
- }
-
- /**
- * Compute the new expiry date adjusted up for a new fresh write.
- *
- * Every time data is written to the memory store for a given line, the
- * relevance is bumped up by a certain amount, which will boost the priority
- * of this line for computation of group attributes, and delay (possibly
- * indefinitely, if the line is accessed regularly) forgetting the data stored
- * in that line.
- * As opposed to bumpExpiryDuration, this function takes the old timestamp and returns the
- * new timestamp.
- *
- * {@see bumpExpiryDuration}, and keep in mind that the bump depends on when this is called,
- * because the relevance decays exponentially, therefore bumping up a high relevance (for a
- * date far in the future) is less potent than bumping up a low relevance (for a date in
- * a close future).
- *
- * @param oldExpiryDate the old date of expiration.
- * @return the new expiration date after the relevance bump.
- */
- public static long bumpExpiryDate(final long oldExpiryDate) {
- final long now = System.currentTimeMillis();
- final long newDuration = bumpExpiryDuration(oldExpiryDate - now);
- return now + newDuration;
- }
-}
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/Utils.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/Utils.java
deleted file mode 100644
index 9cbf490..0000000
--- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/Utils.java
+++ /dev/null
@@ -1,52 +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.server.connectivity.ipmemorystore;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.ipmemorystore.Blob;
-
-/** {@hide} */
-public class Utils {
- /** Pretty print */
- public static String blobToString(@Nullable final Blob blob) {
- return "Blob : " + byteArrayToString(null == blob ? null : blob.data);
- }
-
- /** Pretty print */
- public static String byteArrayToString(@Nullable final byte[] data) {
- if (null == data) return "null";
- final StringBuilder sb = new StringBuilder("[");
- if (data.length <= 24) {
- appendByteArray(sb, data, 0, data.length);
- } else {
- appendByteArray(sb, data, 0, 16);
- sb.append("...");
- appendByteArray(sb, data, data.length - 8, data.length);
- }
- sb.append("]");
- return sb.toString();
- }
-
- // Adds the hex representation of the array between the specified indices (inclusive, exclusive)
- private static void appendByteArray(@NonNull final StringBuilder sb, @NonNull final byte[] ar,
- final int from, final int to) {
- for (int i = from; i < to; ++i) {
- sb.append(String.format("%02X", ar[i]));
- }
- }
-}
diff --git a/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java b/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java
deleted file mode 100644
index 804765e..0000000
--- a/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java
+++ /dev/null
@@ -1,142 +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.server.util;
-
-import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
-
-import java.net.Inet4Address;
-
-/**
- * Network constants used by the network stack.
- */
-public final class NetworkStackConstants {
-
- /**
- * IPv4 constants.
- *
- * See also:
- * - https://tools.ietf.org/html/rfc791
- */
- public static final int IPV4_ADDR_BITS = 32;
- public static final int IPV4_MIN_MTU = 68;
- public static final int IPV4_MAX_MTU = 65_535;
-
- /**
- * Ethernet constants.
- *
- * See also:
- * - https://tools.ietf.org/html/rfc894
- * - https://tools.ietf.org/html/rfc2464
- * - https://tools.ietf.org/html/rfc7042
- * - http://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml
- * - http://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml
- */
- public static final int ETHER_DST_ADDR_OFFSET = 0;
- public static final int ETHER_SRC_ADDR_OFFSET = 6;
- public static final int ETHER_ADDR_LEN = 6;
- public static final int ETHER_TYPE_OFFSET = 12;
- public static final int ETHER_TYPE_LENGTH = 2;
- public static final int ETHER_TYPE_ARP = 0x0806;
- public static final int ETHER_TYPE_IPV4 = 0x0800;
- public static final int ETHER_TYPE_IPV6 = 0x86dd;
- public static final int ETHER_HEADER_LEN = 14;
-
- /**
- * ARP constants.
- *
- * See also:
- * - https://tools.ietf.org/html/rfc826
- * - http://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml
- */
- public static final int ARP_PAYLOAD_LEN = 28; // For Ethernet+IPv4.
- public static final int ARP_REQUEST = 1;
- public static final int ARP_REPLY = 2;
- public static final int ARP_HWTYPE_RESERVED_LO = 0;
- public static final int ARP_HWTYPE_ETHER = 1;
- public static final int ARP_HWTYPE_RESERVED_HI = 0xffff;
-
- /**
- * IPv4 constants.
- *
- * See also:
- * - https://tools.ietf.org/html/rfc791
- */
- public static final int IPV4_HEADER_MIN_LEN = 20;
- public static final int IPV4_IHL_MASK = 0xf;
- public static final int IPV4_FLAGS_OFFSET = 6;
- public static final int IPV4_FRAGMENT_MASK = 0x1fff;
- public static final int IPV4_PROTOCOL_OFFSET = 9;
- public static final int IPV4_SRC_ADDR_OFFSET = 12;
- public static final int IPV4_DST_ADDR_OFFSET = 16;
- public static final int IPV4_ADDR_LEN = 4;
- public static final Inet4Address IPV4_ADDR_ALL = intToInet4AddressHTH(0xffffffff);
- public static final Inet4Address IPV4_ADDR_ANY = intToInet4AddressHTH(0x0);
-
- /**
- * IPv6 constants.
- *
- * See also:
- * - https://tools.ietf.org/html/rfc2460
- */
- public static final int IPV6_ADDR_LEN = 16;
- public static final int IPV6_HEADER_LEN = 40;
- public static final int IPV6_PROTOCOL_OFFSET = 6;
- public static final int IPV6_SRC_ADDR_OFFSET = 8;
- public static final int IPV6_DST_ADDR_OFFSET = 24;
-
- /**
- * ICMPv6 constants.
- *
- * See also:
- * - https://tools.ietf.org/html/rfc4443
- * - https://tools.ietf.org/html/rfc4861
- */
- public static final int ICMPV6_HEADER_MIN_LEN = 4;
- public static final int ICMPV6_ECHO_REPLY_TYPE = 129;
- public static final int ICMPV6_ECHO_REQUEST_TYPE = 128;
- public static final int ICMPV6_ROUTER_SOLICITATION = 133;
- public static final int ICMPV6_ROUTER_ADVERTISEMENT = 134;
- public static final int ICMPV6_NEIGHBOR_SOLICITATION = 135;
- public static final int ICMPV6_NEIGHBOR_ADVERTISEMENT = 136;
- public static final int ICMPV6_ND_OPTION_MIN_LENGTH = 8;
- public static final int ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR = 8;
- public static final int ICMPV6_ND_OPTION_SLLA = 1;
- public static final int ICMPV6_ND_OPTION_TLLA = 2;
- public static final int ICMPV6_ND_OPTION_MTU = 5;
-
- /**
- * UDP constants.
- *
- * See also:
- * - https://tools.ietf.org/html/rfc768
- */
- public static final int UDP_HEADER_LEN = 8;
-
-
- /**
- * DHCP constants.
- *
- * See also:
- * - https://tools.ietf.org/html/rfc2131
- */
- public static final int INFINITE_LEASE = 0xffffffff;
- public static final int DHCP4_CLIENT_PORT = 68;
-
- private NetworkStackConstants() {
- throw new UnsupportedOperationException("This class is not to be instantiated");
- }
-}
diff --git a/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java b/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
deleted file mode 100644
index 6701384..0000000
--- a/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
+++ /dev/null
@@ -1,84 +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.server.util;
-
-import static android.os.Binder.getCallingPid;
-import static android.os.Binder.getCallingUid;
-
-import android.os.Process;
-import android.os.UserHandle;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Utility class to check calling permissions on the network stack.
- */
-public final class PermissionUtil {
- private static final AtomicInteger sSystemPid = new AtomicInteger(-1);
-
- /**
- * Check that the caller is allowed to communicate with the network stack.
- * @throws SecurityException The caller is not allowed to communicate with the network stack.
- */
- public static void checkNetworkStackCallingPermission() {
- final int caller = getCallingUid();
- if (caller == Process.SYSTEM_UID) {
- checkConsistentSystemPid();
- return;
- }
-
- if (UserHandle.getAppId(caller) != Process.BLUETOOTH_UID) {
- throw new SecurityException("Invalid caller: " + caller);
- }
- }
-
- private static void checkConsistentSystemPid() {
- // Apart from the system server process, no process with a system UID should try to
- // communicate with the network stack. This is to ensure that the network stack does not
- // need to maintain behavior for clients it was not designed to work with.
- // Checking that all calls from a system UID originate from the same PID loosely enforces
- // this restriction as if another system process calls the network stack first, the system
- // server would lose access to the network stack and cause obvious failures. If the system
- // server calls the network stack first, other clients would lose access as expected.
- final int systemPid = getCallingPid();
- if (sSystemPid.compareAndSet(-1, systemPid)) {
- // sSystemPid was unset (-1): this was the first call
- return;
- }
-
- if (sSystemPid.get() != systemPid) {
- throw new SecurityException("Invalid PID for the system server, expected "
- + sSystemPid.get() + " but was called from " + systemPid);
- }
- }
-
- /**
- * Check that the caller is allowed to dump the network stack, e.g. dumpsys.
- * @throws SecurityException The caller is not allowed to dump the network stack.
- */
- public static void checkDumpPermission() {
- final int caller = getCallingUid();
- if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID
- && caller != Process.SHELL_UID) {
- throw new SecurityException("No dump permissions for caller: " + caller);
- }
- }
-
- private PermissionUtil() {
- throw new UnsupportedOperationException("This class is not to be instantiated");
- }
-}
diff --git a/packages/NetworkStack/tests/unit/Android.bp b/packages/NetworkStack/tests/unit/Android.bp
deleted file mode 100644
index 6cc8054..0000000
--- a/packages/NetworkStack/tests/unit/Android.bp
+++ /dev/null
@@ -1,103 +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.
-//
-
-android_test {
- name: "NetworkStackTests",
- certificate: "platform",
- srcs: ["src/**/*.java"],
- test_suites: ["device-tests"],
- resource_dirs: ["res"],
- static_libs: [
- "androidx.test.rules",
- "mockito-target-extended-minus-junit4",
- "NetworkStackBase",
- "testables",
- ],
- libs: [
- "android.test.runner",
- "android.test.base",
- "android.test.mock",
- ],
- jni_libs: [
- // For mockito extended
- "libdexmakerjvmtiagent",
- "libstaticjvmtiagent",
- // For ApfTest
- "libartbase",
- "libbacktrace",
- "libbase",
- "libbinder",
- "libbinderthreadstate",
- "libc++",
- "libcgrouprc",
- "libcrypto",
- "libcutils",
- "libdexfile",
- "ld-android",
- "libdl_android",
- "libhidl-gen-utils",
- "libhidlbase",
- "libhidltransport",
- "libhwbinder",
- "libjsoncpp",
- "liblog",
- "liblzma",
- "libnativehelper",
- "libnativehelper_compat_libc++",
- "libnetworkstacktestsjni",
- "libnetworkstackutilsjni",
- "libpackagelistparser",
- "libpcre2",
- "libprocessgroup",
- "libselinux",
- "libui",
- "libutils",
- "libvintf",
- "libvndksupport",
- "libtinyxml2",
- "libunwindstack",
- "libutilscallstack",
- "libziparchive",
- "libz",
- "netd_aidl_interface-V2-cpp",
- ],
-}
-
-cc_library_shared {
- name: "libnetworkstacktestsjni",
- srcs: [
- "jni/**/*.cpp"
- ],
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- ],
- include_dirs: [
- "hardware/google/apf",
- ],
- shared_libs: [
- "libbinder",
- "liblog",
- "libcutils",
- "libnativehelper",
- "netd_aidl_interface-V2-cpp",
- ],
- static_libs: [
- "libapf",
- "libpcap",
- ],
-}
diff --git a/packages/NetworkStack/tests/unit/AndroidManifest.xml b/packages/NetworkStack/tests/unit/AndroidManifest.xml
deleted file mode 100644
index 5dcf6ff..0000000
--- a/packages/NetworkStack/tests/unit/AndroidManifest.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?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.server.networkstack.tests">
-
- <uses-permission android:name="android.permission.READ_LOGS" />
- <uses-permission android:name="android.permission.WRITE_SETTINGS" />
- <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
- <uses-permission android:name="android.permission.READ_PHONE_STATE" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.BROADCAST_STICKY" />
- <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
- <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
- <uses-permission android:name="android.permission.WAKE_LOCK" />
- <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
- <uses-permission android:name="android.permission.REAL_GET_TASKS" />
- <uses-permission android:name="android.permission.GET_DETAILED_TASKS" />
- <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
- <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
- <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
- <uses-permission android:name="android.permission.MANAGE_USERS" />
- <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
- <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
- <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
- <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
- <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
- <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
- <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
- <uses-permission android:name="android.permission.NETWORK_STACK" />
-
- <application android:debuggable="true">
- <uses-library android:name="android.test.runner" />
- </application>
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.networkstack.tests"
- android:label="Networking service tests">
- </instrumentation>
-</manifest>
\ No newline at end of file
diff --git a/packages/NetworkStack/tests/unit/AndroidTest.xml b/packages/NetworkStack/tests/unit/AndroidTest.xml
deleted file mode 100644
index 047bc2e..0000000
--- a/packages/NetworkStack/tests/unit/AndroidTest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
--->
-<configuration description="Runs Tests for NetworkStack">
- <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
- <option name="test-file-name" value="NetworkStackTests.apk" />
- </target_preparer>
-
- <option name="test-suite-tag" value="apct" />
- <option name="test-suite-tag" value="framework-base-presubmit" />
- <option name="test-tag" value="NetworkStackTests" />
- <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="com.android.server.networkstack.tests" />
- <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
- <option name="hidden-api-checks" value="false"/>
- </test>
-</configuration>
\ No newline at end of file
diff --git a/packages/NetworkStack/tests/unit/jni/apf_jni.cpp b/packages/NetworkStack/tests/unit/jni/apf_jni.cpp
deleted file mode 100644
index 4222adf..0000000
--- a/packages/NetworkStack/tests/unit/jni/apf_jni.cpp
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright 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 <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedUtfChars.h>
-#include <jni.h>
-#include <pcap.h>
-#include <stdlib.h>
-#include <string>
-#include <utils/Log.h>
-#include <vector>
-
-#include "apf_interpreter.h"
-#include "nativehelper/scoped_primitive_array.h"
-
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-
-// JNI function acting as simply call-through to native APF interpreter.
-static jint com_android_server_ApfTest_apfSimulate(
- JNIEnv* env, jclass, jbyteArray jprogram, jbyteArray jpacket,
- jbyteArray jdata, jint filter_age) {
-
- ScopedByteArrayRO packet(env, jpacket);
- uint32_t packet_len = (uint32_t)packet.size();
- uint32_t program_len = env->GetArrayLength(jprogram);
- uint32_t data_len = jdata ? env->GetArrayLength(jdata) : 0;
- std::vector<uint8_t> buf(program_len + data_len, 0);
-
- env->GetByteArrayRegion(jprogram, 0, program_len, reinterpret_cast<jbyte*>(buf.data()));
- if (jdata) {
- // Merge program and data into a single buffer.
- env->GetByteArrayRegion(jdata, 0, data_len,
- reinterpret_cast<jbyte*>(buf.data() + program_len));
- }
-
- jint result =
- accept_packet(buf.data(), program_len, program_len + data_len,
- reinterpret_cast<const uint8_t*>(packet.get()), packet_len, filter_age);
-
- if (jdata) {
- env->SetByteArrayRegion(jdata, 0, data_len,
- reinterpret_cast<jbyte*>(buf.data() + program_len));
- }
-
- return result;
-}
-
-class ScopedPcap {
- public:
- explicit ScopedPcap(pcap_t* pcap) : pcap_ptr(pcap) {}
- ~ScopedPcap() {
- pcap_close(pcap_ptr);
- }
-
- pcap_t* get() const { return pcap_ptr; };
- private:
- pcap_t* const pcap_ptr;
-};
-
-class ScopedFILE {
- public:
- explicit ScopedFILE(FILE* fp) : file(fp) {}
- ~ScopedFILE() {
- fclose(file);
- }
-
- FILE* get() const { return file; };
- private:
- FILE* const file;
-};
-
-static void throwException(JNIEnv* env, const std::string& error) {
- jclass newExcCls = env->FindClass("java/lang/IllegalStateException");
- if (newExcCls == 0) {
- abort();
- return;
- }
- env->ThrowNew(newExcCls, error.c_str());
-}
-
-static jstring com_android_server_ApfTest_compileToBpf(JNIEnv* env, jclass, jstring jfilter) {
- ScopedUtfChars filter(env, jfilter);
- std::string bpf_string;
- ScopedPcap pcap(pcap_open_dead(DLT_EN10MB, 65535));
- if (pcap.get() == NULL) {
- throwException(env, "pcap_open_dead failed");
- return NULL;
- }
-
- // Compile "filter" to a BPF program
- bpf_program bpf;
- if (pcap_compile(pcap.get(), &bpf, filter.c_str(), 0, PCAP_NETMASK_UNKNOWN)) {
- throwException(env, "pcap_compile failed");
- return NULL;
- }
-
- // Translate BPF program to human-readable format
- const struct bpf_insn* insn = bpf.bf_insns;
- for (uint32_t i = 0; i < bpf.bf_len; i++) {
- bpf_string += bpf_image(insn++, i);
- bpf_string += "\n";
- }
-
- return env->NewStringUTF(bpf_string.c_str());
-}
-
-static jboolean com_android_server_ApfTest_compareBpfApf(JNIEnv* env, jclass, jstring jfilter,
- jstring jpcap_filename, jbyteArray japf_program) {
- ScopedUtfChars filter(env, jfilter);
- ScopedUtfChars pcap_filename(env, jpcap_filename);
- ScopedByteArrayRO apf_program(env, japf_program);
-
- // Open pcap file for BPF filtering
- ScopedFILE bpf_fp(fopen(pcap_filename.c_str(), "rb"));
- char pcap_error[PCAP_ERRBUF_SIZE];
- ScopedPcap bpf_pcap(pcap_fopen_offline(bpf_fp.get(), pcap_error));
- if (bpf_pcap.get() == NULL) {
- throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error));
- return false;
- }
-
- // Open pcap file for APF filtering
- ScopedFILE apf_fp(fopen(pcap_filename.c_str(), "rb"));
- ScopedPcap apf_pcap(pcap_fopen_offline(apf_fp.get(), pcap_error));
- if (apf_pcap.get() == NULL) {
- throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error));
- return false;
- }
-
- // Compile "filter" to a BPF program
- bpf_program bpf;
- if (pcap_compile(bpf_pcap.get(), &bpf, filter.c_str(), 0, PCAP_NETMASK_UNKNOWN)) {
- throwException(env, "pcap_compile failed");
- return false;
- }
-
- // Install BPF filter on bpf_pcap
- if (pcap_setfilter(bpf_pcap.get(), &bpf)) {
- throwException(env, "pcap_setfilter failed");
- return false;
- }
-
- while (1) {
- pcap_pkthdr bpf_header, apf_header;
- // Run BPF filter to the next matching packet.
- const uint8_t* bpf_packet = pcap_next(bpf_pcap.get(), &bpf_header);
-
- // Run APF filter to the next matching packet.
- const uint8_t* apf_packet;
- do {
- apf_packet = pcap_next(apf_pcap.get(), &apf_header);
- } while (apf_packet != NULL && !accept_packet(
- reinterpret_cast<uint8_t*>(const_cast<int8_t*>(apf_program.get())),
- apf_program.size(), 0 /* data_len */,
- apf_packet, apf_header.len, 0 /* filter_age */));
-
- // Make sure both filters matched the same packet.
- if (apf_packet == NULL && bpf_packet == NULL)
- break;
- if (apf_packet == NULL || bpf_packet == NULL)
- return false;
- if (apf_header.len != bpf_header.len ||
- apf_header.ts.tv_sec != bpf_header.ts.tv_sec ||
- apf_header.ts.tv_usec != bpf_header.ts.tv_usec ||
- memcmp(apf_packet, bpf_packet, apf_header.len))
- return false;
- }
- return true;
-}
-
-static jboolean com_android_server_ApfTest_dropsAllPackets(JNIEnv* env, jclass, jbyteArray jprogram,
- jbyteArray jdata, jstring jpcap_filename) {
- ScopedUtfChars pcap_filename(env, jpcap_filename);
- ScopedByteArrayRO apf_program(env, jprogram);
- uint32_t apf_program_len = (uint32_t)apf_program.size();
- uint32_t data_len = env->GetArrayLength(jdata);
- pcap_pkthdr apf_header;
- const uint8_t* apf_packet;
- char pcap_error[PCAP_ERRBUF_SIZE];
- std::vector<uint8_t> buf(apf_program_len + data_len, 0);
-
- // Merge program and data into a single buffer.
- env->GetByteArrayRegion(jprogram, 0, apf_program_len, reinterpret_cast<jbyte*>(buf.data()));
- env->GetByteArrayRegion(jdata, 0, data_len,
- reinterpret_cast<jbyte*>(buf.data() + apf_program_len));
-
- // Open pcap file
- ScopedFILE apf_fp(fopen(pcap_filename.c_str(), "rb"));
- ScopedPcap apf_pcap(pcap_fopen_offline(apf_fp.get(), pcap_error));
-
- if (apf_pcap.get() == NULL) {
- throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error));
- return false;
- }
-
- while ((apf_packet = pcap_next(apf_pcap.get(), &apf_header)) != NULL) {
- int result = accept_packet(buf.data(), apf_program_len,
- apf_program_len + data_len, apf_packet, apf_header.len, 0);
-
- // Return false once packet passes the filter
- if (result) {
- env->SetByteArrayRegion(jdata, 0, data_len,
- reinterpret_cast<jbyte*>(buf.data() + apf_program_len));
- return false;
- }
- }
-
- env->SetByteArrayRegion(jdata, 0, data_len,
- reinterpret_cast<jbyte*>(buf.data() + apf_program_len));
- return true;
-}
-
-extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
- JNIEnv *env;
- if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
- ALOGE("ERROR: GetEnv failed");
- return -1;
- }
-
- static JNINativeMethod gMethods[] = {
- { "apfSimulate", "([B[B[BI)I",
- (void*)com_android_server_ApfTest_apfSimulate },
- { "compileToBpf", "(Ljava/lang/String;)Ljava/lang/String;",
- (void*)com_android_server_ApfTest_compileToBpf },
- { "compareBpfApf", "(Ljava/lang/String;Ljava/lang/String;[B)Z",
- (void*)com_android_server_ApfTest_compareBpfApf },
- { "dropsAllPackets", "([B[BLjava/lang/String;)Z",
- (void*)com_android_server_ApfTest_dropsAllPackets },
- };
-
- jniRegisterNativeMethods(env, "android/net/apf/ApfTest",
- gMethods, ARRAY_SIZE(gMethods));
-
- return JNI_VERSION_1_6;
-}
diff --git a/packages/NetworkStack/tests/unit/res/raw/apf.pcap b/packages/NetworkStack/tests/unit/res/raw/apf.pcap
deleted file mode 100644
index 963165f..0000000
--- a/packages/NetworkStack/tests/unit/res/raw/apf.pcap
+++ /dev/null
Binary files differ
diff --git a/packages/NetworkStack/tests/unit/res/raw/apfPcap.pcap b/packages/NetworkStack/tests/unit/res/raw/apfPcap.pcap
deleted file mode 100644
index 6f69c4a..0000000
--- a/packages/NetworkStack/tests/unit/res/raw/apfPcap.pcap
+++ /dev/null
Binary files differ
diff --git a/packages/NetworkStack/tests/unit/src/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/unit/src/android/net/apf/ApfTest.java
deleted file mode 100644
index 8f2b968..0000000
--- a/packages/NetworkStack/tests/unit/src/android/net/apf/ApfTest.java
+++ /dev/null
@@ -1,2110 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.apf;
-
-import static android.system.OsConstants.AF_UNIX;
-import static android.system.OsConstants.ARPHRD_ETHER;
-import static android.system.OsConstants.ETH_P_ARP;
-import static android.system.OsConstants.ETH_P_IP;
-import static android.system.OsConstants.ETH_P_IPV6;
-import static android.system.OsConstants.IPPROTO_ICMPV6;
-import static android.system.OsConstants.IPPROTO_TCP;
-import static android.system.OsConstants.IPPROTO_UDP;
-import static android.system.OsConstants.SOCK_STREAM;
-
-import static com.android.internal.util.BitUtils.bytesToBEInt;
-import static com.android.server.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.NattKeepalivePacketDataParcelable;
-import android.net.TcpKeepalivePacketDataParcelable;
-import android.net.apf.ApfFilter.ApfConfiguration;
-import android.net.apf.ApfGenerator.IllegalInstructionException;
-import android.net.apf.ApfGenerator.Register;
-import android.net.ip.IIpClientCallbacks;
-import android.net.ip.IpClient.IpClientCallbacksWrapper;
-import android.net.metrics.IpConnectivityLog;
-import android.net.metrics.RaEvent;
-import android.net.util.InterfaceParams;
-import android.net.util.SharedLog;
-import android.os.ConditionVariable;
-import android.os.Parcelable;
-import android.os.SystemClock;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.text.format.DateUtils;
-import android.util.Log;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.util.HexDump;
-import com.android.server.networkstack.tests.R;
-import com.android.server.util.NetworkStackConstants;
-
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.nio.ByteBuffer;
-import java.util.List;
-import java.util.Random;
-
-/**
- * Tests for APF program generator and interpreter.
- *
- * Build, install and run with:
- * runtest frameworks-net -c android.net.apf.ApfTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class ApfTest {
- private static final int TIMEOUT_MS = 500;
- private static final int MIN_APF_VERSION = 2;
-
- @Mock IpConnectivityLog mLog;
- @Mock Context mContext;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- // Load up native shared library containing APF interpreter exposed via JNI.
- System.loadLibrary("networkstacktestsjni");
- }
-
- private static final String TAG = "ApfTest";
- // Expected return codes from APF interpreter.
- private static final int PASS = 1;
- private static final int DROP = 0;
- // Interpreter will just accept packets without link layer headers, so pad fake packet to at
- // least the minimum packet size.
- private static final int MIN_PKT_SIZE = 15;
-
- private static final ApfCapabilities MOCK_APF_CAPABILITIES =
- new ApfCapabilities(2, 1700, ARPHRD_ETHER);
-
- private static final boolean DROP_MULTICAST = true;
- private static final boolean ALLOW_MULTICAST = false;
-
- private static final boolean DROP_802_3_FRAMES = true;
- private static final boolean ALLOW_802_3_FRAMES = false;
-
- // Constants for opcode encoding
- private static final byte LI_OP = (byte)(13 << 3);
- private static final byte LDDW_OP = (byte)(22 << 3);
- private static final byte STDW_OP = (byte)(23 << 3);
- private static final byte SIZE0 = (byte)(0 << 1);
- private static final byte SIZE8 = (byte)(1 << 1);
- private static final byte SIZE16 = (byte)(2 << 1);
- private static final byte SIZE32 = (byte)(3 << 1);
- private static final byte R1 = 1;
-
- private static ApfConfiguration getDefaultConfig() {
- ApfFilter.ApfConfiguration config = new ApfConfiguration();
- config.apfCapabilities = MOCK_APF_CAPABILITIES;
- config.multicastFilter = ALLOW_MULTICAST;
- config.ieee802_3Filter = ALLOW_802_3_FRAMES;
- config.ethTypeBlackList = new int[0];
- return config;
- }
-
- private static String label(int code) {
- switch (code) {
- case PASS: return "PASS";
- case DROP: return "DROP";
- default: return "UNKNOWN";
- }
- }
-
- private static void assertReturnCodesEqual(int expected, int got) {
- assertEquals(label(expected), label(got));
- }
-
- private void assertVerdict(int expected, byte[] program, byte[] packet, int filterAge) {
- assertReturnCodesEqual(expected, apfSimulate(program, packet, null, filterAge));
- }
-
- private void assertVerdict(int expected, byte[] program, byte[] packet) {
- assertReturnCodesEqual(expected, apfSimulate(program, packet, null, 0));
- }
-
- private void assertPass(byte[] program, byte[] packet, int filterAge) {
- assertVerdict(PASS, program, packet, filterAge);
- }
-
- private void assertPass(byte[] program, byte[] packet) {
- assertVerdict(PASS, program, packet);
- }
-
- private void assertDrop(byte[] program, byte[] packet, int filterAge) {
- assertVerdict(DROP, program, packet, filterAge);
- }
-
- private void assertDrop(byte[] program, byte[] packet) {
- assertVerdict(DROP, program, packet);
- }
-
- private void assertProgramEquals(byte[] expected, byte[] program) throws AssertionError {
- // assertArrayEquals() would only print one byte, making debugging difficult.
- if (!java.util.Arrays.equals(expected, program)) {
- throw new AssertionError(
- "\nexpected: " + HexDump.toHexString(expected) +
- "\nactual: " + HexDump.toHexString(program));
- }
- }
-
- private void assertDataMemoryContents(
- int expected, byte[] program, byte[] packet, byte[] data, byte[] expected_data)
- throws IllegalInstructionException, Exception {
- assertReturnCodesEqual(expected, apfSimulate(program, packet, data, 0 /* filterAge */));
-
- // assertArrayEquals() would only print one byte, making debugging difficult.
- if (!java.util.Arrays.equals(expected_data, data)) {
- throw new Exception(
- "\nprogram: " + HexDump.toHexString(program) +
- "\ndata memory: " + HexDump.toHexString(data) +
- "\nexpected: " + HexDump.toHexString(expected_data));
- }
- }
-
- private void assertVerdict(int expected, ApfGenerator gen, byte[] packet, int filterAge)
- throws IllegalInstructionException {
- assertReturnCodesEqual(expected, apfSimulate(gen.generate(), packet, null,
- filterAge));
- }
-
- private void assertPass(ApfGenerator gen, byte[] packet, int filterAge)
- throws IllegalInstructionException {
- assertVerdict(PASS, gen, packet, filterAge);
- }
-
- private void assertDrop(ApfGenerator gen, byte[] packet, int filterAge)
- throws IllegalInstructionException {
- assertVerdict(DROP, gen, packet, filterAge);
- }
-
- private void assertPass(ApfGenerator gen)
- throws IllegalInstructionException {
- assertVerdict(PASS, gen, new byte[MIN_PKT_SIZE], 0);
- }
-
- private void assertDrop(ApfGenerator gen)
- throws IllegalInstructionException {
- assertVerdict(DROP, gen, new byte[MIN_PKT_SIZE], 0);
- }
-
- /**
- * Test each instruction by generating a program containing the instruction,
- * generating bytecode for that program and running it through the
- * interpreter to verify it functions correctly.
- */
- @Test
- public void testApfInstructions() throws IllegalInstructionException {
- // Empty program should pass because having the program counter reach the
- // location immediately after the program indicates the packet should be
- // passed to the AP.
- ApfGenerator gen = new ApfGenerator(MIN_APF_VERSION);
- assertPass(gen);
-
- // Test jumping to pass label.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJump(gen.PASS_LABEL);
- byte[] program = gen.generate();
- assertEquals(1, program.length);
- assertEquals((14 << 3) | (0 << 1) | 0, program[0]);
- assertPass(program, new byte[MIN_PKT_SIZE], 0);
-
- // Test jumping to drop label.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJump(gen.DROP_LABEL);
- program = gen.generate();
- assertEquals(2, program.length);
- assertEquals((14 << 3) | (1 << 1) | 0, program[0]);
- assertEquals(1, program[1]);
- assertDrop(program, new byte[15], 15);
-
- // Test jumping if equal to 0.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test jumping if not equal to 0.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL);
- assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1);
- gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test jumping if registers equal.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0EqualsR1(gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test jumping if registers not equal.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL);
- assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1);
- gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test load immediate.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test add.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addAdd(1234567890);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test subtract.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addAdd(-1234567890);
- gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test or.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addOr(1234567890);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test and.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addAnd(123456789);
- gen.addJumpIfR0Equals(1234567890 & 123456789, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test left shift.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addLeftShift(1);
- gen.addJumpIfR0Equals(1234567890 << 1, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test right shift.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addRightShift(1);
- gen.addJumpIfR0Equals(1234567890 >> 1, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test multiply.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 123456789);
- gen.addMul(2);
- gen.addJumpIfR0Equals(123456789 * 2, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test divide.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addDiv(2);
- gen.addJumpIfR0Equals(1234567890 / 2, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test divide by zero.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addDiv(0);
- gen.addJump(gen.DROP_LABEL);
- assertPass(gen);
-
- // Test add.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 1234567890);
- gen.addAddR1();
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test subtract.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, -1234567890);
- gen.addAddR1();
- gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test or.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 1234567890);
- gen.addOrR1();
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test and.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addLoadImmediate(Register.R1, 123456789);
- gen.addAndR1();
- gen.addJumpIfR0Equals(1234567890 & 123456789, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test left shift.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addLoadImmediate(Register.R1, 1);
- gen.addLeftShiftR1();
- gen.addJumpIfR0Equals(1234567890 << 1, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test right shift.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addLoadImmediate(Register.R1, -1);
- gen.addLeftShiftR1();
- gen.addJumpIfR0Equals(1234567890 >> 1, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test multiply.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 123456789);
- gen.addLoadImmediate(Register.R1, 2);
- gen.addMulR1();
- gen.addJumpIfR0Equals(123456789 * 2, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test divide.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addLoadImmediate(Register.R1, 2);
- gen.addDivR1();
- gen.addJumpIfR0Equals(1234567890 / 2, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test divide by zero.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addDivR1();
- gen.addJump(gen.DROP_LABEL);
- assertPass(gen);
-
- // Test byte load.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoad8(Register.R0, 1);
- gen.addJumpIfR0Equals(45, gen.DROP_LABEL);
- assertDrop(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
-
- // Test out of bounds load.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoad8(Register.R0, 16);
- gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
- assertPass(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
-
- // Test half-word load.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoad16(Register.R0, 1);
- gen.addJumpIfR0Equals((45 << 8) | 67, gen.DROP_LABEL);
- assertDrop(gen, new byte[]{123,45,67,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
-
- // Test word load.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoad32(Register.R0, 1);
- gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, gen.DROP_LABEL);
- assertDrop(gen, new byte[]{123,45,67,89,12,0,0,0,0,0,0,0,0,0,0}, 0);
-
- // Test byte indexed load.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 1);
- gen.addLoad8Indexed(Register.R0, 0);
- gen.addJumpIfR0Equals(45, gen.DROP_LABEL);
- assertDrop(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
-
- // Test out of bounds indexed load.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 8);
- gen.addLoad8Indexed(Register.R0, 8);
- gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
- assertPass(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
-
- // Test half-word indexed load.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 1);
- gen.addLoad16Indexed(Register.R0, 0);
- gen.addJumpIfR0Equals((45 << 8) | 67, gen.DROP_LABEL);
- assertDrop(gen, new byte[]{123,45,67,0,0,0,0,0,0,0,0,0,0,0,0}, 0);
-
- // Test word indexed load.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 1);
- gen.addLoad32Indexed(Register.R0, 0);
- gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, gen.DROP_LABEL);
- assertDrop(gen, new byte[]{123,45,67,89,12,0,0,0,0,0,0,0,0,0,0}, 0);
-
- // Test jumping if greater than.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL);
- assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1);
- gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test jumping if less than.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0LessThan(0, gen.DROP_LABEL);
- assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0LessThan(1, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test jumping if any bits set.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
- assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1);
- gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
- assertDrop(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 3);
- gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test jumping if register greater than.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL);
- assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 2);
- gen.addLoadImmediate(Register.R1, 1);
- gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test jumping if register less than.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfR0LessThanR1(gen.DROP_LABEL);
- assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 1);
- gen.addJumpIfR0LessThanR1(gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test jumping if any bits set in register.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 3);
- gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
- assertPass(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 3);
- gen.addLoadImmediate(Register.R0, 1);
- gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
- assertDrop(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 3);
- gen.addLoadImmediate(Register.R0, 3);
- gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test load from memory.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadFromMemory(Register.R0, 0);
- gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test store to memory.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 1234567890);
- gen.addStoreToMemory(Register.R1, 12);
- gen.addLoadFromMemory(Register.R0, 12);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test filter age pre-filled memory.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen, new byte[MIN_PKT_SIZE], 1234567890);
-
- // Test packet size pre-filled memory.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT);
- gen.addJumpIfR0Equals(MIN_PKT_SIZE, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test IPv4 header size pre-filled memory.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
- gen.addJumpIfR0Equals(20, gen.DROP_LABEL);
- assertDrop(gen, new byte[]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x45}, 0);
-
- // Test not.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addNot(Register.R0);
- gen.addJumpIfR0Equals(~1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test negate.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addNeg(Register.R0);
- gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test move.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 1234567890);
- gen.addMove(Register.R0);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addMove(Register.R1);
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test swap.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R1, 1234567890);
- gen.addSwap();
- gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL);
- assertDrop(gen);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1234567890);
- gen.addSwap();
- gen.addJumpIfR0Equals(0, gen.DROP_LABEL);
- assertDrop(gen);
-
- // Test jump if bytes not equal.
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1);
- gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
- program = gen.generate();
- assertEquals(6, program.length);
- assertEquals((13 << 3) | (1 << 1) | 0, program[0]);
- assertEquals(1, program[1]);
- assertEquals(((20 << 3) | (1 << 1) | 0) - 256, program[2]);
- assertEquals(1, program[3]);
- assertEquals(1, program[4]);
- assertEquals(123, program[5]);
- assertDrop(program, new byte[MIN_PKT_SIZE], 0);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1);
- gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
- byte[] packet123 = {0,123,0,0,0,0,0,0,0,0,0,0,0,0,0};
- assertPass(gen, packet123, 0);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
- assertDrop(gen, packet123, 0);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1);
- gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{1,2,30,4,5}, gen.DROP_LABEL);
- byte[] packet12345 = {0,1,2,3,4,5,0,0,0,0,0,0,0,0,0};
- assertDrop(gen, packet12345, 0);
- gen = new ApfGenerator(MIN_APF_VERSION);
- gen.addLoadImmediate(Register.R0, 1);
- gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{1,2,3,4,5}, gen.DROP_LABEL);
- assertPass(gen, packet12345, 0);
- }
-
- @Test(expected = ApfGenerator.IllegalInstructionException.class)
- public void testApfGeneratorWantsV2OrGreater() throws Exception {
- // The minimum supported APF version is 2.
- new ApfGenerator(1);
- }
-
- @Test
- public void testApfDataOpcodesWantApfV3() throws IllegalInstructionException, Exception {
- ApfGenerator gen = new ApfGenerator(MIN_APF_VERSION);
- try {
- gen.addStoreData(Register.R0, 0);
- fail();
- } catch (IllegalInstructionException expected) {
- /* pass */
- }
- try {
- gen.addLoadData(Register.R0, 0);
- fail();
- } catch (IllegalInstructionException expected) {
- /* pass */
- }
- }
-
- /**
- * Test that the generator emits immediates using the shortest possible encoding.
- */
- @Test
- public void testImmediateEncoding() throws IllegalInstructionException {
- ApfGenerator gen;
-
- // 0-byte immediate: li R0, 0
- gen = new ApfGenerator(4);
- gen.addLoadImmediate(Register.R0, 0);
- assertProgramEquals(new byte[]{LI_OP | SIZE0}, gen.generate());
-
- // 1-byte immediate: li R0, 42
- gen = new ApfGenerator(4);
- gen.addLoadImmediate(Register.R0, 42);
- assertProgramEquals(new byte[]{LI_OP | SIZE8, 42}, gen.generate());
-
- // 2-byte immediate: li R1, 0x1234
- gen = new ApfGenerator(4);
- gen.addLoadImmediate(Register.R1, 0x1234);
- assertProgramEquals(new byte[]{LI_OP | SIZE16 | R1, 0x12, 0x34}, gen.generate());
-
- // 4-byte immediate: li R0, 0x12345678
- gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R0, 0x12345678);
- assertProgramEquals(
- new byte[]{LI_OP | SIZE32, 0x12, 0x34, 0x56, 0x78},
- gen.generate());
- }
-
- /**
- * Test that the generator emits negative immediates using the shortest possible encoding.
- */
- @Test
- public void testNegativeImmediateEncoding() throws IllegalInstructionException {
- ApfGenerator gen;
-
- // 1-byte negative immediate: li R0, -42
- gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R0, -42);
- assertProgramEquals(new byte[]{LI_OP | SIZE8, -42}, gen.generate());
-
- // 2-byte negative immediate: li R1, -0x1122
- gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R1, -0x1122);
- assertProgramEquals(new byte[]{LI_OP | SIZE16 | R1, (byte)0xEE, (byte)0xDE},
- gen.generate());
-
- // 4-byte negative immediate: li R0, -0x11223344
- gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R0, -0x11223344);
- assertProgramEquals(
- new byte[]{LI_OP | SIZE32, (byte)0xEE, (byte)0xDD, (byte)0xCC, (byte)0xBC},
- gen.generate());
- }
-
- /**
- * Test that the generator correctly emits positive and negative immediates for LDDW/STDW.
- */
- @Test
- public void testLoadStoreDataEncoding() throws IllegalInstructionException {
- ApfGenerator gen;
-
- // Load data with no offset: lddw R0, [0 + r1]
- gen = new ApfGenerator(3);
- gen.addLoadData(Register.R0, 0);
- assertProgramEquals(new byte[]{LDDW_OP | SIZE0}, gen.generate());
-
- // Store data with 8bit negative offset: lddw r0, [-42 + r1]
- gen = new ApfGenerator(3);
- gen.addStoreData(Register.R0, -42);
- assertProgramEquals(new byte[]{STDW_OP | SIZE8, -42}, gen.generate());
-
- // Store data to R1 with 16bit negative offset: stdw r1, [-0x1122 + r0]
- gen = new ApfGenerator(3);
- gen.addStoreData(Register.R1, -0x1122);
- assertProgramEquals(new byte[]{STDW_OP | SIZE16 | R1, (byte)0xEE, (byte)0xDE},
- gen.generate());
-
- // Load data to R1 with 32bit negative offset: lddw r1, [0xDEADBEEF + r0]
- gen = new ApfGenerator(3);
- gen.addLoadData(Register.R1, 0xDEADBEEF);
- assertProgramEquals(
- new byte[]{LDDW_OP | SIZE32 | R1, (byte)0xDE, (byte)0xAD, (byte)0xBE, (byte)0xEF},
- gen.generate());
- }
-
- /**
- * Test that the interpreter correctly executes STDW with a negative 8bit offset
- */
- @Test
- public void testApfDataWrite() throws IllegalInstructionException, Exception {
- byte[] packet = new byte[MIN_PKT_SIZE];
- byte[] data = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
- byte[] expected_data = data.clone();
-
- // No memory access instructions: should leave the data segment untouched.
- ApfGenerator gen = new ApfGenerator(3);
- assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
-
- // Expect value 0x87654321 to be stored starting from address -11 from the end of the
- // data buffer, in big-endian order.
- gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R0, 0x87654321);
- gen.addLoadImmediate(Register.R1, -5);
- gen.addStoreData(Register.R0, -6); // -5 + -6 = -11 (offset +5 with data_len=16)
- expected_data[5] = (byte)0x87;
- expected_data[6] = (byte)0x65;
- expected_data[7] = (byte)0x43;
- expected_data[8] = (byte)0x21;
- assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
- }
-
- /**
- * Test that the interpreter correctly executes LDDW with a negative 16bit offset
- */
- @Test
- public void testApfDataRead() throws IllegalInstructionException, Exception {
- // Program that DROPs if address 10 (-6) contains 0x87654321.
- ApfGenerator gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R1, 1000);
- gen.addLoadData(Register.R0, -1006); // 1000 + -1006 = -6 (offset +10 with data_len=16)
- gen.addJumpIfR0Equals(0x87654321, gen.DROP_LABEL);
- byte[] program = gen.generate();
- byte[] packet = new byte[MIN_PKT_SIZE];
-
- // Content is incorrect (last byte does not match) -> PASS
- byte[] data = new byte[16];
- data[10] = (byte)0x87;
- data[11] = (byte)0x65;
- data[12] = (byte)0x43;
- data[13] = (byte)0x00; // != 0x21
- byte[] expected_data = data.clone();
- assertDataMemoryContents(PASS, program, packet, data, expected_data);
-
- // Fix the last byte -> conditional jump taken -> DROP
- data[13] = (byte)0x21;
- expected_data = data;
- assertDataMemoryContents(DROP, program, packet, data, expected_data);
- }
-
- /**
- * Test that the interpreter correctly executes LDDW followed by a STDW.
- * To cover a few more edge cases, LDDW has a 0bit offset, while STDW has a positive 8bit
- * offset.
- */
- @Test
- public void testApfDataReadModifyWrite() throws IllegalInstructionException, Exception {
- ApfGenerator gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R1, -22);
- gen.addLoadData(Register.R0, 0); // Load from address 32 -22 + 0 = 10
- gen.addAdd(0x78453412); // 87654321 + 78453412 = FFAA7733
- gen.addStoreData(Register.R0, 4); // Write back to address 32 -22 + 4 = 14
-
- byte[] packet = new byte[MIN_PKT_SIZE];
- byte[] data = new byte[32];
- data[10] = (byte)0x87;
- data[11] = (byte)0x65;
- data[12] = (byte)0x43;
- data[13] = (byte)0x21;
- byte[] expected_data = data.clone();
- expected_data[14] = (byte)0xFF;
- expected_data[15] = (byte)0xAA;
- expected_data[16] = (byte)0x77;
- expected_data[17] = (byte)0x33;
- assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
- }
-
- @Test
- public void testApfDataBoundChecking() throws IllegalInstructionException, Exception {
- byte[] packet = new byte[MIN_PKT_SIZE];
- byte[] data = new byte[32];
- byte[] expected_data = data;
-
- // Program that DROPs unconditionally. This is our the baseline.
- ApfGenerator gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R0, 3);
- gen.addLoadData(Register.R1, 7);
- gen.addJump(gen.DROP_LABEL);
- assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data);
-
- // Same program as before, but this time we're trying to load past the end of the data.
- gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R0, 20);
- gen.addLoadData(Register.R1, 15); // 20 + 15 > 32
- gen.addJump(gen.DROP_LABEL); // Not reached.
- assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
-
- // Subtracting an immediate should work...
- gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R0, 20);
- gen.addLoadData(Register.R1, -4);
- gen.addJump(gen.DROP_LABEL);
- assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data);
-
- // ...and underflowing simply wraps around to the end of the buffer...
- gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R0, 20);
- gen.addLoadData(Register.R1, -30);
- gen.addJump(gen.DROP_LABEL);
- assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data);
-
- // ...but doesn't allow accesses before the start of the buffer
- gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R0, 20);
- gen.addLoadData(Register.R1, -1000);
- gen.addJump(gen.DROP_LABEL); // Not reached.
- assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
- }
-
- /**
- * Generate some BPF programs, translate them to APF, then run APF and BPF programs
- * over packet traces and verify both programs filter out the same packets.
- */
- @Test
- public void testApfAgainstBpf() throws Exception {
- String[] tcpdump_filters = new String[]{ "udp", "tcp", "icmp", "icmp6", "udp port 53",
- "arp", "dst 239.255.255.250", "arp or tcp or udp port 53", "net 192.168.1.0/24",
- "arp or icmp6 or portrange 53-54", "portrange 53-54 or portrange 100-50000",
- "tcp[tcpflags] & (tcp-ack|tcp-fin) != 0 and (ip[2:2] > 57 or icmp)" };
- String pcap_filename = stageFile(R.raw.apf);
- for (String tcpdump_filter : tcpdump_filters) {
- byte[] apf_program = Bpf2Apf.convert(compileToBpf(tcpdump_filter));
- assertTrue("Failed to match for filter: " + tcpdump_filter,
- compareBpfApf(tcpdump_filter, pcap_filename, apf_program));
- }
- }
-
- /**
- * Generate APF program, run pcap file though APF filter, then check all the packets in the file
- * should be dropped.
- */
- @Test
- public void testApfFilterPcapFile() throws Exception {
- final byte[] MOCK_PCAP_IPV4_ADDR = {(byte) 172, 16, 7, (byte) 151};
- String pcapFilename = stageFile(R.raw.apfPcap);
- MockIpClientCallback ipClientCallback = new MockIpClientCallback();
- LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_PCAP_IPV4_ADDR), 16);
- LinkProperties lp = new LinkProperties();
- lp.addLinkAddress(link);
-
- ApfConfiguration config = getDefaultConfig();
- ApfCapabilities MOCK_APF_PCAP_CAPABILITIES = new ApfCapabilities(4, 1700, ARPHRD_ETHER);
- config.apfCapabilities = MOCK_APF_PCAP_CAPABILITIES;
- config.multicastFilter = DROP_MULTICAST;
- config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
- apfFilter.setLinkProperties(lp);
- byte[] program = ipClientCallback.getApfProgram();
- byte[] data = new byte[ApfFilter.Counter.totalSize()];
- final boolean result;
-
- result = dropsAllPackets(program, data, pcapFilename);
- Log.i(TAG, "testApfFilterPcapFile(): Data counters: " + HexDump.toHexString(data, false));
-
- assertTrue("Failed to drop all packets by filter. \nAPF counters:" +
- HexDump.toHexString(data, false), result);
- }
-
- private class MockIpClientCallback extends IpClientCallbacksWrapper {
- private final ConditionVariable mGotApfProgram = new ConditionVariable();
- private byte[] mLastApfProgram;
-
- MockIpClientCallback() {
- super(mock(IIpClientCallbacks.class), mock(SharedLog.class));
- }
-
- @Override
- public void installPacketFilter(byte[] filter) {
- mLastApfProgram = filter;
- mGotApfProgram.open();
- }
-
- public void resetApfProgramWait() {
- mGotApfProgram.close();
- }
-
- public byte[] getApfProgram() {
- assertTrue(mGotApfProgram.block(TIMEOUT_MS));
- return mLastApfProgram;
- }
-
- public void assertNoProgramUpdate() {
- assertFalse(mGotApfProgram.block(TIMEOUT_MS));
- }
- }
-
- private static class TestApfFilter extends ApfFilter {
- public static final byte[] MOCK_MAC_ADDR = {1,2,3,4,5,6};
-
- private FileDescriptor mWriteSocket;
- private final long mFixedTimeMs = SystemClock.elapsedRealtime();
-
- public TestApfFilter(Context context, ApfConfiguration config,
- IpClientCallbacksWrapper ipClientCallback, IpConnectivityLog log) throws Exception {
- super(context, config, InterfaceParams.getByName("lo"), ipClientCallback, log);
- }
-
- // Pretend an RA packet has been received and show it to ApfFilter.
- public void pretendPacketReceived(byte[] packet) throws IOException, ErrnoException {
- // ApfFilter's ReceiveThread will be waiting to read this.
- Os.write(mWriteSocket, packet, 0, packet.length);
- }
-
- @Override
- protected long currentTimeSeconds() {
- return mFixedTimeMs / DateUtils.SECOND_IN_MILLIS;
- }
-
- @Override
- void maybeStartFilter() {
- mHardwareAddress = MOCK_MAC_ADDR;
- installNewProgramLocked();
-
- // Create two sockets, "readSocket" and "mWriteSocket" and connect them together.
- FileDescriptor readSocket = new FileDescriptor();
- mWriteSocket = new FileDescriptor();
- try {
- Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mWriteSocket, readSocket);
- } catch (ErrnoException e) {
- fail();
- return;
- }
- // Now pass readSocket to ReceiveThread as if it was setup to read raw RAs.
- // This allows us to pretend RA packets have been recieved via pretendPacketReceived().
- mReceiveThread = new ReceiveThread(readSocket);
- mReceiveThread.start();
- }
-
- @Override
- public void shutdown() {
- super.shutdown();
- IoUtils.closeQuietly(mWriteSocket);
- }
- }
-
- private static final int ETH_HEADER_LEN = 14;
- private static final int ETH_DEST_ADDR_OFFSET = 0;
- private static final int ETH_ETHERTYPE_OFFSET = 12;
- private static final byte[] ETH_BROADCAST_MAC_ADDRESS =
- {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
-
- private static final int IPV4_HEADER_LEN = 20;
- private static final int IPV4_VERSION_IHL_OFFSET = ETH_HEADER_LEN + 0;
- private static final int IPV4_TOTAL_LENGTH_OFFSET = ETH_HEADER_LEN + 2;
- private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
- private static final int IPV4_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 12;
- private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
-
- private static final int IPV4_TCP_HEADER_LEN = 20;
- private static final int IPV4_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN;
- private static final int IPV4_TCP_SRC_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 0;
- private static final int IPV4_TCP_DEST_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 2;
- private static final int IPV4_TCP_SEQ_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 4;
- private static final int IPV4_TCP_ACK_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 8;
- private static final int IPV4_TCP_HEADER_LENGTH_OFFSET = IPV4_TCP_HEADER_OFFSET + 12;
- private static final int IPV4_TCP_HEADER_FLAG_OFFSET = IPV4_TCP_HEADER_OFFSET + 13;
-
- private static final int IPV4_UDP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN;;
- private static final int IPV4_UDP_SRC_PORT_OFFSET = IPV4_UDP_HEADER_OFFSET + 0;
- private static final int IPV4_UDP_DEST_PORT_OFFSET = IPV4_UDP_HEADER_OFFSET + 2;
- private static final int IPV4_UDP_LENGTH_OFFSET = IPV4_UDP_HEADER_OFFSET + 4;
- private static final int IPV4_UDP_PAYLOAD_OFFSET = IPV4_UDP_HEADER_OFFSET + 8;
- private static final byte[] IPV4_BROADCAST_ADDRESS =
- {(byte) 255, (byte) 255, (byte) 255, (byte) 255};
-
- private static final int IPV6_HEADER_LEN = 40;
- private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
- private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
- private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
- private static final int IPV6_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
- private static final int IPV6_TCP_SRC_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 0;
- private static final int IPV6_TCP_DEST_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 2;
- private static final int IPV6_TCP_SEQ_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 4;
- private static final int IPV6_TCP_ACK_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 8;
- // The IPv6 all nodes address ff02::1
- private static final byte[] IPV6_ALL_NODES_ADDRESS =
- { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
- private static final byte[] IPV6_ALL_ROUTERS_ADDRESS =
- { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 };
-
- private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
- private static final int ICMP6_ROUTER_SOLICITATION = 133;
- private static final int ICMP6_ROUTER_ADVERTISEMENT = 134;
- private static final int ICMP6_NEIGHBOR_SOLICITATION = 135;
- private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136;
-
- private static final int ICMP6_RA_HEADER_LEN = 16;
- private static final int ICMP6_RA_ROUTER_LIFETIME_OFFSET =
- ETH_HEADER_LEN + IPV6_HEADER_LEN + 6;
- private static final int ICMP6_RA_CHECKSUM_OFFSET =
- ETH_HEADER_LEN + IPV6_HEADER_LEN + 2;
- private static final int ICMP6_RA_OPTION_OFFSET =
- ETH_HEADER_LEN + IPV6_HEADER_LEN + ICMP6_RA_HEADER_LEN;
-
- private static final int ICMP6_PREFIX_OPTION_TYPE = 3;
- private static final int ICMP6_PREFIX_OPTION_LEN = 32;
- private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4;
- private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET = 8;
-
- // From RFC6106: Recursive DNS Server option
- private static final int ICMP6_RDNSS_OPTION_TYPE = 25;
- // From RFC6106: DNS Search List option
- private static final int ICMP6_DNSSL_OPTION_TYPE = 31;
-
- // From RFC4191: Route Information option
- private static final int ICMP6_ROUTE_INFO_OPTION_TYPE = 24;
- // Above three options all have the same format:
- private static final int ICMP6_4_BYTE_OPTION_LEN = 8;
- private static final int ICMP6_4_BYTE_LIFETIME_OFFSET = 4;
- private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4;
-
- private static final int UDP_HEADER_LEN = 8;
- private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 22;
-
- private static final int DHCP_CLIENT_PORT = 68;
- private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 48;
-
- private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
- private static final byte[] ARP_IPV4_REQUEST_HEADER = {
- 0, 1, // Hardware type: Ethernet (1)
- 8, 0, // Protocol type: IP (0x0800)
- 6, // Hardware size: 6
- 4, // Protocol size: 4
- 0, 1 // Opcode: request (1)
- };
- private static final byte[] ARP_IPV4_REPLY_HEADER = {
- 0, 1, // Hardware type: Ethernet (1)
- 8, 0, // Protocol type: IP (0x0800)
- 6, // Hardware size: 6
- 4, // Protocol size: 4
- 0, 2 // Opcode: reply (2)
- };
- private static final int ARP_SOURCE_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 14;
- private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 24;
-
- private static final byte[] MOCK_IPV4_ADDR = {10, 0, 0, 1};
- private static final byte[] MOCK_BROADCAST_IPV4_ADDR = {10, 0, 31, (byte) 255}; // prefix = 19
- private static final byte[] MOCK_MULTICAST_IPV4_ADDR = {(byte) 224, 0, 0, 1};
- private static final byte[] ANOTHER_IPV4_ADDR = {10, 0, 0, 2};
- private static final byte[] IPV4_SOURCE_ADDR = {10, 0, 0, 3};
- private static final byte[] ANOTHER_IPV4_SOURCE_ADDR = {(byte) 192, 0, 2, 1};
- private static final byte[] BUG_PROBE_SOURCE_ADDR1 = {0, 0, 1, 2};
- private static final byte[] BUG_PROBE_SOURCE_ADDR2 = {3, 4, 0, 0};
- private static final byte[] IPV4_ANY_HOST_ADDR = {0, 0, 0, 0};
-
- // Helper to initialize a default apfFilter.
- private ApfFilter setupApfFilter(
- IpClientCallbacksWrapper ipClientCallback, ApfConfiguration config) throws Exception {
- LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
- LinkProperties lp = new LinkProperties();
- lp.addLinkAddress(link);
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
- apfFilter.setLinkProperties(lp);
- return apfFilter;
- }
-
- @Test
- public void testApfFilterIPv4() throws Exception {
- MockIpClientCallback ipClientCallback = new MockIpClientCallback();
- LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
- LinkProperties lp = new LinkProperties();
- lp.addLinkAddress(link);
-
- ApfConfiguration config = getDefaultConfig();
- config.multicastFilter = DROP_MULTICAST;
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
- apfFilter.setLinkProperties(lp);
-
- byte[] program = ipClientCallback.getApfProgram();
-
- // Verify empty packet of 100 zero bytes is passed
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- assertPass(program, packet.array());
-
- // Verify unicast IPv4 packet is passed
- put(packet, ETH_DEST_ADDR_OFFSET, TestApfFilter.MOCK_MAC_ADDR);
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_IPV4_ADDR);
- assertPass(program, packet.array());
-
- // Verify L2 unicast to IPv4 broadcast addresses is dropped (b/30231088)
- put(packet, IPV4_DEST_ADDR_OFFSET, IPV4_BROADCAST_ADDRESS);
- assertDrop(program, packet.array());
- put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_BROADCAST_IPV4_ADDR);
- assertDrop(program, packet.array());
-
- // Verify multicast/broadcast IPv4, not DHCP to us, is dropped
- put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS);
- assertDrop(program, packet.array());
- packet.put(IPV4_VERSION_IHL_OFFSET, (byte)0x45);
- assertDrop(program, packet.array());
- packet.put(IPV4_PROTOCOL_OFFSET, (byte)IPPROTO_UDP);
- assertDrop(program, packet.array());
- packet.putShort(UDP_DESTINATION_PORT_OFFSET, (short)DHCP_CLIENT_PORT);
- assertDrop(program, packet.array());
- put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_MULTICAST_IPV4_ADDR);
- assertDrop(program, packet.array());
- put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_BROADCAST_IPV4_ADDR);
- assertDrop(program, packet.array());
- put(packet, IPV4_DEST_ADDR_OFFSET, IPV4_BROADCAST_ADDRESS);
- assertDrop(program, packet.array());
-
- // Verify broadcast IPv4 DHCP to us is passed
- put(packet, DHCP_CLIENT_MAC_OFFSET, TestApfFilter.MOCK_MAC_ADDR);
- assertPass(program, packet.array());
-
- // Verify unicast IPv4 DHCP to us is passed
- put(packet, ETH_DEST_ADDR_OFFSET, TestApfFilter.MOCK_MAC_ADDR);
- assertPass(program, packet.array());
-
- apfFilter.shutdown();
- }
-
- @Test
- public void testApfFilterIPv6() throws Exception {
- MockIpClientCallback ipClientCallback = new MockIpClientCallback();
- ApfConfiguration config = getDefaultConfig();
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
- byte[] program = ipClientCallback.getApfProgram();
-
- // Verify empty IPv6 packet is passed
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- assertPass(program, packet.array());
-
- // Verify empty ICMPv6 packet is passed
- packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
- assertPass(program, packet.array());
-
- // Verify empty ICMPv6 NA packet is passed
- packet.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_NEIGHBOR_ANNOUNCEMENT);
- assertPass(program, packet.array());
-
- // Verify ICMPv6 NA to ff02::1 is dropped
- put(packet, IPV6_DEST_ADDR_OFFSET, IPV6_ALL_NODES_ADDRESS);
- assertDrop(program, packet.array());
-
- // Verify ICMPv6 RS to any is dropped
- packet.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_ROUTER_SOLICITATION);
- assertDrop(program, packet.array());
- put(packet, IPV6_DEST_ADDR_OFFSET, IPV6_ALL_ROUTERS_ADDRESS);
- assertDrop(program, packet.array());
-
- apfFilter.shutdown();
- }
-
- @Test
- public void testApfFilterMulticast() throws Exception {
- final byte[] unicastIpv4Addr = {(byte)192,0,2,63};
- final byte[] broadcastIpv4Addr = {(byte)192,0,2,(byte)255};
- final byte[] multicastIpv4Addr = {(byte)224,0,0,1};
- final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb};
-
- MockIpClientCallback ipClientCallback = new MockIpClientCallback();
- LinkAddress link = new LinkAddress(InetAddress.getByAddress(unicastIpv4Addr), 24);
- LinkProperties lp = new LinkProperties();
- lp.addLinkAddress(link);
-
- ApfConfiguration config = getDefaultConfig();
- config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
- apfFilter.setLinkProperties(lp);
-
- byte[] program = ipClientCallback.getApfProgram();
-
- // Construct IPv4 and IPv6 multicast packets.
- ByteBuffer mcastv4packet = ByteBuffer.wrap(new byte[100]);
- mcastv4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- put(mcastv4packet, IPV4_DEST_ADDR_OFFSET, multicastIpv4Addr);
-
- ByteBuffer mcastv6packet = ByteBuffer.wrap(new byte[100]);
- mcastv6packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- mcastv6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_UDP);
- put(mcastv6packet, IPV6_DEST_ADDR_OFFSET, multicastIpv6Addr);
-
- // Construct IPv4 broadcast packet.
- ByteBuffer bcastv4packet1 = ByteBuffer.wrap(new byte[100]);
- bcastv4packet1.put(ETH_BROADCAST_MAC_ADDRESS);
- bcastv4packet1.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- put(bcastv4packet1, IPV4_DEST_ADDR_OFFSET, multicastIpv4Addr);
-
- ByteBuffer bcastv4packet2 = ByteBuffer.wrap(new byte[100]);
- bcastv4packet2.put(ETH_BROADCAST_MAC_ADDRESS);
- bcastv4packet2.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- put(bcastv4packet2, IPV4_DEST_ADDR_OFFSET, IPV4_BROADCAST_ADDRESS);
-
- // Construct IPv4 broadcast with L2 unicast address packet (b/30231088).
- ByteBuffer bcastv4unicastl2packet = ByteBuffer.wrap(new byte[100]);
- bcastv4unicastl2packet.put(TestApfFilter.MOCK_MAC_ADDR);
- bcastv4unicastl2packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- put(bcastv4unicastl2packet, IPV4_DEST_ADDR_OFFSET, broadcastIpv4Addr);
-
- // Verify initially disabled multicast filter is off
- assertPass(program, mcastv4packet.array());
- assertPass(program, mcastv6packet.array());
- assertPass(program, bcastv4packet1.array());
- assertPass(program, bcastv4packet2.array());
- assertPass(program, bcastv4unicastl2packet.array());
-
- // Turn on multicast filter and verify it works
- ipClientCallback.resetApfProgramWait();
- apfFilter.setMulticastFilter(true);
- program = ipClientCallback.getApfProgram();
- assertDrop(program, mcastv4packet.array());
- assertDrop(program, mcastv6packet.array());
- assertDrop(program, bcastv4packet1.array());
- assertDrop(program, bcastv4packet2.array());
- assertDrop(program, bcastv4unicastl2packet.array());
-
- // Turn off multicast filter and verify it's off
- ipClientCallback.resetApfProgramWait();
- apfFilter.setMulticastFilter(false);
- program = ipClientCallback.getApfProgram();
- assertPass(program, mcastv4packet.array());
- assertPass(program, mcastv6packet.array());
- assertPass(program, bcastv4packet1.array());
- assertPass(program, bcastv4packet2.array());
- assertPass(program, bcastv4unicastl2packet.array());
-
- // Verify it can be initialized to on
- ipClientCallback.resetApfProgramWait();
- apfFilter.shutdown();
- config.multicastFilter = DROP_MULTICAST;
- config.ieee802_3Filter = DROP_802_3_FRAMES;
- apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
- apfFilter.setLinkProperties(lp);
- program = ipClientCallback.getApfProgram();
- assertDrop(program, mcastv4packet.array());
- assertDrop(program, mcastv6packet.array());
- assertDrop(program, bcastv4packet1.array());
- assertDrop(program, bcastv4unicastl2packet.array());
-
- // Verify that ICMPv6 multicast is not dropped.
- mcastv6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
- assertPass(program, mcastv6packet.array());
-
- apfFilter.shutdown();
- }
-
- @Test
- public void testApfFilterMulticastPingWhileDozing() throws Exception {
- MockIpClientCallback ipClientCallback = new MockIpClientCallback();
- ApfFilter apfFilter = setupApfFilter(ipClientCallback, getDefaultConfig());
-
- // Construct a multicast ICMPv6 ECHO request.
- final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb};
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
- packet.put(ICMP6_TYPE_OFFSET, (byte)ICMPV6_ECHO_REQUEST_TYPE);
- put(packet, IPV6_DEST_ADDR_OFFSET, multicastIpv6Addr);
-
- // Normally, we let multicast pings alone...
- assertPass(ipClientCallback.getApfProgram(), packet.array());
-
- // ...and even while dozing...
- apfFilter.setDozeMode(true);
- assertPass(ipClientCallback.getApfProgram(), packet.array());
-
- // ...but when the multicast filter is also enabled, drop the multicast pings to save power.
- apfFilter.setMulticastFilter(true);
- assertDrop(ipClientCallback.getApfProgram(), packet.array());
-
- // However, we should still let through all other ICMPv6 types.
- ByteBuffer raPacket = ByteBuffer.wrap(packet.array().clone());
- raPacket.put(ICMP6_TYPE_OFFSET, (byte) NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT);
- assertPass(ipClientCallback.getApfProgram(), raPacket.array());
-
- // Now wake up from doze mode to ensure that we no longer drop the packets.
- // (The multicast filter is still enabled at this point).
- apfFilter.setDozeMode(false);
- assertPass(ipClientCallback.getApfProgram(), packet.array());
-
- apfFilter.shutdown();
- }
-
- @Test
- public void testApfFilter802_3() throws Exception {
- MockIpClientCallback ipClientCallback = new MockIpClientCallback();
- ApfConfiguration config = getDefaultConfig();
- ApfFilter apfFilter = setupApfFilter(ipClientCallback, config);
- byte[] program = ipClientCallback.getApfProgram();
-
- // Verify empty packet of 100 zero bytes is passed
- // Note that eth-type = 0 makes it an IEEE802.3 frame
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- assertPass(program, packet.array());
-
- // Verify empty packet with IPv4 is passed
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- assertPass(program, packet.array());
-
- // Verify empty IPv6 packet is passed
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- assertPass(program, packet.array());
-
- // Now turn on the filter
- ipClientCallback.resetApfProgramWait();
- apfFilter.shutdown();
- config.ieee802_3Filter = DROP_802_3_FRAMES;
- apfFilter = setupApfFilter(ipClientCallback, config);
- program = ipClientCallback.getApfProgram();
-
- // Verify that IEEE802.3 frame is dropped
- // In this case ethtype is used for payload length
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)(100 - 14));
- assertDrop(program, packet.array());
-
- // Verify that IPv4 (as example of Ethernet II) frame will pass
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- assertPass(program, packet.array());
-
- // Verify that IPv6 (as example of Ethernet II) frame will pass
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- assertPass(program, packet.array());
-
- apfFilter.shutdown();
- }
-
- @Test
- public void testApfFilterEthTypeBL() throws Exception {
- final int[] emptyBlackList = {};
- final int[] ipv4BlackList = {ETH_P_IP};
- final int[] ipv4Ipv6BlackList = {ETH_P_IP, ETH_P_IPV6};
-
- MockIpClientCallback ipClientCallback = new MockIpClientCallback();
- ApfConfiguration config = getDefaultConfig();
- ApfFilter apfFilter = setupApfFilter(ipClientCallback, config);
- byte[] program = ipClientCallback.getApfProgram();
-
- // Verify empty packet of 100 zero bytes is passed
- // Note that eth-type = 0 makes it an IEEE802.3 frame
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- assertPass(program, packet.array());
-
- // Verify empty packet with IPv4 is passed
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- assertPass(program, packet.array());
-
- // Verify empty IPv6 packet is passed
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- assertPass(program, packet.array());
-
- // Now add IPv4 to the black list
- ipClientCallback.resetApfProgramWait();
- apfFilter.shutdown();
- config.ethTypeBlackList = ipv4BlackList;
- apfFilter = setupApfFilter(ipClientCallback, config);
- program = ipClientCallback.getApfProgram();
-
- // Verify that IPv4 frame will be dropped
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- assertDrop(program, packet.array());
-
- // Verify that IPv6 frame will pass
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- assertPass(program, packet.array());
-
- // Now let us have both IPv4 and IPv6 in the black list
- ipClientCallback.resetApfProgramWait();
- apfFilter.shutdown();
- config.ethTypeBlackList = ipv4Ipv6BlackList;
- apfFilter = setupApfFilter(ipClientCallback, config);
- program = ipClientCallback.getApfProgram();
-
- // Verify that IPv4 frame will be dropped
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- assertDrop(program, packet.array());
-
- // Verify that IPv6 frame will be dropped
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- assertDrop(program, packet.array());
-
- apfFilter.shutdown();
- }
-
- private byte[] getProgram(MockIpClientCallback cb, ApfFilter filter, LinkProperties lp) {
- cb.resetApfProgramWait();
- filter.setLinkProperties(lp);
- return cb.getApfProgram();
- }
-
- private void verifyArpFilter(byte[] program, int filterResult) {
- // Verify ARP request packet
- assertPass(program, arpRequestBroadcast(MOCK_IPV4_ADDR));
- assertVerdict(filterResult, program, arpRequestBroadcast(ANOTHER_IPV4_ADDR));
- assertDrop(program, arpRequestBroadcast(IPV4_ANY_HOST_ADDR));
-
- // Verify ARP reply packets from different source ip
- assertDrop(program, arpReply(IPV4_ANY_HOST_ADDR, IPV4_ANY_HOST_ADDR));
- assertPass(program, arpReply(ANOTHER_IPV4_SOURCE_ADDR, IPV4_ANY_HOST_ADDR));
- assertPass(program, arpReply(BUG_PROBE_SOURCE_ADDR1, IPV4_ANY_HOST_ADDR));
- assertPass(program, arpReply(BUG_PROBE_SOURCE_ADDR2, IPV4_ANY_HOST_ADDR));
-
- // Verify unicast ARP reply packet is always accepted.
- assertPass(program, arpReply(IPV4_SOURCE_ADDR, MOCK_IPV4_ADDR));
- assertPass(program, arpReply(IPV4_SOURCE_ADDR, ANOTHER_IPV4_ADDR));
- assertPass(program, arpReply(IPV4_SOURCE_ADDR, IPV4_ANY_HOST_ADDR));
-
- // Verify GARP reply packets are always filtered
- assertDrop(program, garpReply());
- }
-
- @Test
- public void testApfFilterArp() throws Exception {
- MockIpClientCallback ipClientCallback = new MockIpClientCallback();
- ApfConfiguration config = getDefaultConfig();
- config.multicastFilter = DROP_MULTICAST;
- config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
-
- // Verify initially ARP request filter is off, and GARP filter is on.
- verifyArpFilter(ipClientCallback.getApfProgram(), PASS);
-
- // Inform ApfFilter of our address and verify ARP filtering is on
- LinkAddress linkAddress = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 24);
- LinkProperties lp = new LinkProperties();
- assertTrue(lp.addLinkAddress(linkAddress));
- verifyArpFilter(getProgram(ipClientCallback, apfFilter, lp), DROP);
-
- // Inform ApfFilter of loss of IP and verify ARP filtering is off
- verifyArpFilter(getProgram(ipClientCallback, apfFilter, new LinkProperties()), PASS);
-
- apfFilter.shutdown();
- }
-
- private static byte[] arpReply(byte[] sip, byte[] tip) {
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
- put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REPLY_HEADER);
- put(packet, ARP_SOURCE_IP_ADDRESS_OFFSET, sip);
- put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, tip);
- return packet.array();
- }
-
- private static byte[] arpRequestBroadcast(byte[] tip) {
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
- put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS);
- put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REQUEST_HEADER);
- put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, tip);
- return packet.array();
- }
-
- private static byte[] garpReply() {
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
- put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS);
- put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REPLY_HEADER);
- put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, IPV4_ANY_HOST_ADDR);
- return packet.array();
- }
-
- private static final byte[] IPV4_KEEPALIVE_SRC_ADDR = {10, 0, 0, 5};
- private static final byte[] IPV4_KEEPALIVE_DST_ADDR = {10, 0, 0, 6};
- private static final byte[] IPV4_ANOTHER_ADDR = {10, 0 , 0, 7};
- private static final byte[] IPV6_KEEPALIVE_SRC_ADDR =
- {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf1};
- private static final byte[] IPV6_KEEPALIVE_DST_ADDR =
- {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf2};
- private static final byte[] IPV6_ANOTHER_ADDR =
- {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf5};
-
- @Test
- public void testApfFilterKeepaliveAck() throws Exception {
- final MockIpClientCallback cb = new MockIpClientCallback();
- final ApfConfiguration config = getDefaultConfig();
- config.multicastFilter = DROP_MULTICAST;
- config.ieee802_3Filter = DROP_802_3_FRAMES;
- final TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog);
- byte[] program;
- final int srcPort = 12345;
- final int dstPort = 54321;
- final int seqNum = 2123456789;
- final int ackNum = 1234567890;
- final int anotherSrcPort = 23456;
- final int anotherDstPort = 65432;
- final int anotherSeqNum = 2123456780;
- final int anotherAckNum = 1123456789;
- final int slot1 = 1;
- final int slot2 = 2;
- final int window = 14480;
- final int windowScale = 4;
-
- // src: 10.0.0.5, port: 12345
- // dst: 10.0.0.6, port: 54321
- InetAddress srcAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_SRC_ADDR);
- InetAddress dstAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_DST_ADDR);
-
- final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable();
- parcel.srcAddress = srcAddr.getAddress();
- parcel.srcPort = srcPort;
- parcel.dstAddress = dstAddr.getAddress();
- parcel.dstPort = dstPort;
- parcel.seq = seqNum;
- parcel.ack = ackNum;
-
- apfFilter.addTcpKeepalivePacketFilter(slot1, parcel);
- program = cb.getApfProgram();
-
- // Verify IPv4 keepalive ack packet is dropped
- // src: 10.0.0.6, port: 54321
- // dst: 10.0.0.5, port: 12345
- assertDrop(program,
- ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
- dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */));
- // Verify IPv4 non-keepalive ack packet from the same source address is passed
- assertPass(program,
- ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
- dstPort, srcPort, ackNum + 100, seqNum, 0 /* dataLength */));
- assertPass(program,
- ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
- dstPort, srcPort, ackNum, seqNum + 1, 10 /* dataLength */));
- // Verify IPv4 packet from another address is passed
- assertPass(program,
- ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort,
- anotherDstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */));
-
- // Remove IPv4 keepalive filter
- apfFilter.removeKeepalivePacketFilter(slot1);
-
- try {
- // src: 2404:0:0:0:0:0:faf1, port: 12345
- // dst: 2404:0:0:0:0:0:faf2, port: 54321
- srcAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_SRC_ADDR);
- dstAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_DST_ADDR);
-
- final TcpKeepalivePacketDataParcelable ipv6Parcel =
- new TcpKeepalivePacketDataParcelable();
- ipv6Parcel.srcAddress = srcAddr.getAddress();
- ipv6Parcel.srcPort = srcPort;
- ipv6Parcel.dstAddress = dstAddr.getAddress();
- ipv6Parcel.dstPort = dstPort;
- ipv6Parcel.seq = seqNum;
- ipv6Parcel.ack = ackNum;
-
- apfFilter.addTcpKeepalivePacketFilter(slot1, ipv6Parcel);
- program = cb.getApfProgram();
-
- // Verify IPv6 keepalive ack packet is dropped
- // src: 2404:0:0:0:0:0:faf2, port: 54321
- // dst: 2404:0:0:0:0:0:faf1, port: 12345
- assertDrop(program,
- ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
- dstPort, srcPort, ackNum, seqNum + 1));
- // Verify IPv6 non-keepalive ack packet from the same source address is passed
- assertPass(program,
- ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
- dstPort, srcPort, ackNum + 100, seqNum));
- // Verify IPv6 packet from another address is passed
- assertPass(program,
- ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, anotherSrcPort,
- anotherDstPort, anotherSeqNum, anotherAckNum));
-
- // Remove IPv6 keepalive filter
- apfFilter.removeKeepalivePacketFilter(slot1);
-
- // Verify multiple filters
- apfFilter.addTcpKeepalivePacketFilter(slot1, parcel);
- apfFilter.addTcpKeepalivePacketFilter(slot2, ipv6Parcel);
- program = cb.getApfProgram();
-
- // Verify IPv4 keepalive ack packet is dropped
- // src: 10.0.0.6, port: 54321
- // dst: 10.0.0.5, port: 12345
- assertDrop(program,
- ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
- dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */));
- // Verify IPv4 non-keepalive ack packet from the same source address is passed
- assertPass(program,
- ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
- dstPort, srcPort, ackNum + 100, seqNum, 0 /* dataLength */));
- // Verify IPv4 packet from another address is passed
- assertPass(program,
- ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort,
- anotherDstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */));
-
- // Verify IPv6 keepalive ack packet is dropped
- // src: 2404:0:0:0:0:0:faf2, port: 54321
- // dst: 2404:0:0:0:0:0:faf1, port: 12345
- assertDrop(program,
- ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
- dstPort, srcPort, ackNum, seqNum + 1));
- // Verify IPv6 non-keepalive ack packet from the same source address is passed
- assertPass(program,
- ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
- dstPort, srcPort, ackNum + 100, seqNum));
- // Verify IPv6 packet from another address is passed
- assertPass(program,
- ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, anotherSrcPort,
- anotherDstPort, anotherSeqNum, anotherAckNum));
-
- // Remove keepalive filters
- apfFilter.removeKeepalivePacketFilter(slot1);
- apfFilter.removeKeepalivePacketFilter(slot2);
- } catch (UnsupportedOperationException e) {
- // TODO: support V6 packets
- }
-
- program = cb.getApfProgram();
-
- // Verify IPv4, IPv6 packets are passed
- assertPass(program,
- ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
- dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */));
- assertPass(program,
- ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
- dstPort, srcPort, ackNum, seqNum + 1));
- assertPass(program,
- ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, srcPort,
- dstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */));
- assertPass(program,
- ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, srcPort,
- dstPort, anotherSeqNum, anotherAckNum));
-
- apfFilter.shutdown();
- }
-
- private static byte[] ipv4Packet(byte[] sip, byte[] dip, int sport,
- int dport, int seq, int ack, int dataLength) {
- final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN;
-
- ByteBuffer packet = ByteBuffer.wrap(new byte[totalLength + ETH_HEADER_LEN]);
-
- // ether type
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP);
-
- // IPv4 header
- packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45);
- packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength);
- packet.put(IPV4_PROTOCOL_OFFSET, (byte) IPPROTO_TCP);
- put(packet, IPV4_SRC_ADDR_OFFSET, sip);
- put(packet, IPV4_DEST_ADDR_OFFSET, dip);
- packet.putShort(IPV4_TCP_SRC_PORT_OFFSET, (short) sport);
- packet.putShort(IPV4_TCP_DEST_PORT_OFFSET, (short) dport);
- packet.putInt(IPV4_TCP_SEQ_NUM_OFFSET, seq);
- packet.putInt(IPV4_TCP_ACK_NUM_OFFSET, ack);
-
- // TCP header length 5(20 bytes), reserved 3 bits, NS=0
- packet.put(IPV4_TCP_HEADER_LENGTH_OFFSET, (byte) 0x50);
- // TCP flags: ACK set
- packet.put(IPV4_TCP_HEADER_FLAG_OFFSET, (byte) 0x10);
- return packet.array();
- }
-
- private static byte[] ipv6Packet(byte[] sip, byte[] tip, int sport,
- int dport, int seq, int ack) {
- ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IPV6);
- put(packet, IPV6_SRC_ADDR_OFFSET, sip);
- put(packet, IPV6_DEST_ADDR_OFFSET, tip);
- packet.putShort(IPV6_TCP_SRC_PORT_OFFSET, (short) sport);
- packet.putShort(IPV6_TCP_DEST_PORT_OFFSET, (short) dport);
- packet.putInt(IPV6_TCP_SEQ_NUM_OFFSET, seq);
- packet.putInt(IPV6_TCP_ACK_NUM_OFFSET, ack);
- return packet.array();
- }
-
- @Test
- public void testApfFilterNattKeepalivePacket() throws Exception {
- final MockIpClientCallback cb = new MockIpClientCallback();
- final ApfConfiguration config = getDefaultConfig();
- config.multicastFilter = DROP_MULTICAST;
- config.ieee802_3Filter = DROP_802_3_FRAMES;
- final TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog);
- byte[] program;
- final int srcPort = 1024;
- final int dstPort = 4500;
- final int slot1 = 1;
- // NAT-T keepalive
- final byte[] kaPayload = {(byte) 0xff};
- final byte[] nonKaPayload = {(byte) 0xfe};
-
- // src: 10.0.0.5, port: 1024
- // dst: 10.0.0.6, port: 4500
- InetAddress srcAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_SRC_ADDR);
- InetAddress dstAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_DST_ADDR);
-
- final NattKeepalivePacketDataParcelable parcel = new NattKeepalivePacketDataParcelable();
- parcel.srcAddress = srcAddr.getAddress();
- parcel.srcPort = srcPort;
- parcel.dstAddress = dstAddr.getAddress();
- parcel.dstPort = dstPort;
-
- apfFilter.addNattKeepalivePacketFilter(slot1, parcel);
- program = cb.getApfProgram();
-
- // Verify IPv4 keepalive packet is dropped
- // src: 10.0.0.6, port: 4500
- // dst: 10.0.0.5, port: 1024
- byte[] pkt = ipv4UdpPacket(IPV4_KEEPALIVE_DST_ADDR,
- IPV4_KEEPALIVE_SRC_ADDR, dstPort, srcPort, 1 /* dataLength */);
- System.arraycopy(kaPayload, 0, pkt, IPV4_UDP_PAYLOAD_OFFSET, kaPayload.length);
- assertDrop(program, pkt);
-
- // Verify a packet with payload length 1 byte but it is not 0xff will pass the filter.
- System.arraycopy(nonKaPayload, 0, pkt, IPV4_UDP_PAYLOAD_OFFSET, nonKaPayload.length);
- assertPass(program, pkt);
-
- // Verify IPv4 non-keepalive response packet from the same source address is passed
- assertPass(program,
- ipv4UdpPacket(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
- dstPort, srcPort, 10 /* dataLength */));
-
- // Verify IPv4 non-keepalive response packet from other source address is passed
- assertPass(program,
- ipv4UdpPacket(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
- dstPort, srcPort, 10 /* dataLength */));
-
- apfFilter.removeKeepalivePacketFilter(slot1);
- apfFilter.shutdown();
- }
-
- private static byte[] ipv4UdpPacket(byte[] sip, byte[] dip, int sport,
- int dport, int dataLength) {
- final int totalLength = dataLength + IPV4_HEADER_LEN + UDP_HEADER_LEN;
- final int udpLength = UDP_HEADER_LEN + dataLength;
- ByteBuffer packet = ByteBuffer.wrap(new byte[totalLength + ETH_HEADER_LEN]);
-
- // ether type
- packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP);
-
- // IPv4 header
- packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45);
- packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength);
- packet.put(IPV4_PROTOCOL_OFFSET, (byte) IPPROTO_UDP);
- put(packet, IPV4_SRC_ADDR_OFFSET, sip);
- put(packet, IPV4_DEST_ADDR_OFFSET, dip);
- packet.putShort(IPV4_UDP_SRC_PORT_OFFSET, (short) sport);
- packet.putShort(IPV4_UDP_DEST_PORT_OFFSET, (short) dport);
- packet.putShort(IPV4_UDP_LENGTH_OFFSET, (short) udpLength);
-
- return packet.array();
- }
-
- // Verify that the last program pushed to the IpClient.Callback properly filters the
- // given packet for the given lifetime.
- private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime) {
- final int FRACTION_OF_LIFETIME = 6;
- final int ageLimit = lifetime / FRACTION_OF_LIFETIME;
-
- // Verify new program should drop RA for 1/6th its lifetime and pass afterwards.
- assertDrop(program, packet.array());
- assertDrop(program, packet.array(), ageLimit);
- assertPass(program, packet.array(), ageLimit + 1);
- assertPass(program, packet.array(), lifetime);
- // Verify RA checksum is ignored
- final short originalChecksum = packet.getShort(ICMP6_RA_CHECKSUM_OFFSET);
- packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, (short)12345);
- assertDrop(program, packet.array());
- packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, (short)-12345);
- assertDrop(program, packet.array());
- packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, originalChecksum);
-
- // Verify other changes to RA make it not match filter
- final byte originalFirstByte = packet.get(0);
- packet.put(0, (byte)-1);
- assertPass(program, packet.array());
- packet.put(0, (byte)0);
- assertDrop(program, packet.array());
- packet.put(0, originalFirstByte);
- }
-
- // Test that when ApfFilter is shown the given packet, it generates a program to filter it
- // for the given lifetime.
- private void verifyRaLifetime(TestApfFilter apfFilter, MockIpClientCallback ipClientCallback,
- ByteBuffer packet, int lifetime) throws IOException, ErrnoException {
- // Verify new program generated if ApfFilter witnesses RA
- ipClientCallback.resetApfProgramWait();
- apfFilter.pretendPacketReceived(packet.array());
- byte[] program = ipClientCallback.getApfProgram();
- verifyRaLifetime(program, packet, lifetime);
- }
-
- private void verifyRaEvent(RaEvent expected) {
- ArgumentCaptor<IpConnectivityLog.Event> captor =
- ArgumentCaptor.forClass(IpConnectivityLog.Event.class);
- verify(mLog, atLeastOnce()).log(captor.capture());
- RaEvent got = lastRaEvent(captor.getAllValues());
- if (!raEventEquals(expected, got)) {
- assertEquals(expected, got); // fail for printing an assertion error message.
- }
- }
-
- private RaEvent lastRaEvent(List<IpConnectivityLog.Event> events) {
- RaEvent got = null;
- for (Parcelable ev : events) {
- if (ev instanceof RaEvent) {
- got = (RaEvent) ev;
- }
- }
- return got;
- }
-
- private boolean raEventEquals(RaEvent ev1, RaEvent ev2) {
- return (ev1 != null) && (ev2 != null)
- && (ev1.routerLifetime == ev2.routerLifetime)
- && (ev1.prefixValidLifetime == ev2.prefixValidLifetime)
- && (ev1.prefixPreferredLifetime == ev2.prefixPreferredLifetime)
- && (ev1.routeInfoLifetime == ev2.routeInfoLifetime)
- && (ev1.rdnssLifetime == ev2.rdnssLifetime)
- && (ev1.dnsslLifetime == ev2.dnsslLifetime);
- }
-
- private void assertInvalidRa(TestApfFilter apfFilter, MockIpClientCallback ipClientCallback,
- ByteBuffer packet) throws IOException, ErrnoException {
- ipClientCallback.resetApfProgramWait();
- apfFilter.pretendPacketReceived(packet.array());
- ipClientCallback.assertNoProgramUpdate();
- }
-
- @Test
- public void testApfFilterRa() throws Exception {
- MockIpClientCallback ipClientCallback = new MockIpClientCallback();
- ApfConfiguration config = getDefaultConfig();
- config.multicastFilter = DROP_MULTICAST;
- config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
- byte[] program = ipClientCallback.getApfProgram();
-
- final int ROUTER_LIFETIME = 1000;
- final int PREFIX_VALID_LIFETIME = 200;
- final int PREFIX_PREFERRED_LIFETIME = 100;
- final int RDNSS_LIFETIME = 300;
- final int ROUTE_LIFETIME = 400;
- // Note that lifetime of 2000 will be ignored in favor of shorter route lifetime of 1000.
- final int DNSSL_LIFETIME = 2000;
- final int VERSION_TRAFFIC_CLASS_FLOW_LABEL_OFFSET = ETH_HEADER_LEN;
- // IPv6, traffic class = 0, flow label = 0x12345
- final int VERSION_TRAFFIC_CLASS_FLOW_LABEL = 0x60012345;
-
- // Verify RA is passed the first time
- ByteBuffer basePacket = ByteBuffer.wrap(new byte[ICMP6_RA_OPTION_OFFSET]);
- basePacket.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- basePacket.putInt(VERSION_TRAFFIC_CLASS_FLOW_LABEL_OFFSET,
- VERSION_TRAFFIC_CLASS_FLOW_LABEL);
- basePacket.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
- basePacket.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_ROUTER_ADVERTISEMENT);
- basePacket.putShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET, (short)ROUTER_LIFETIME);
- basePacket.position(IPV6_DEST_ADDR_OFFSET);
- basePacket.put(IPV6_ALL_NODES_ADDRESS);
- assertPass(program, basePacket.array());
-
- verifyRaLifetime(apfFilter, ipClientCallback, basePacket, ROUTER_LIFETIME);
- verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, -1));
-
- ByteBuffer newFlowLabelPacket = ByteBuffer.wrap(new byte[ICMP6_RA_OPTION_OFFSET]);
- basePacket.clear();
- newFlowLabelPacket.put(basePacket);
- // Check that changes are ignored in every byte of the flow label.
- newFlowLabelPacket.putInt(VERSION_TRAFFIC_CLASS_FLOW_LABEL_OFFSET,
- VERSION_TRAFFIC_CLASS_FLOW_LABEL + 0x11111);
-
- // Ensure zero-length options cause the packet to be silently skipped.
- // Do this before we test other packets. http://b/29586253
- ByteBuffer zeroLengthOptionPacket = ByteBuffer.wrap(
- new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
- basePacket.clear();
- zeroLengthOptionPacket.put(basePacket);
- zeroLengthOptionPacket.put((byte)ICMP6_PREFIX_OPTION_TYPE);
- zeroLengthOptionPacket.put((byte)0);
- assertInvalidRa(apfFilter, ipClientCallback, zeroLengthOptionPacket);
-
- // Generate several RAs with different options and lifetimes, and verify when
- // ApfFilter is shown these packets, it generates programs to filter them for the
- // appropriate lifetime.
- ByteBuffer prefixOptionPacket = ByteBuffer.wrap(
- new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_LEN]);
- basePacket.clear();
- prefixOptionPacket.put(basePacket);
- prefixOptionPacket.put((byte)ICMP6_PREFIX_OPTION_TYPE);
- prefixOptionPacket.put((byte)(ICMP6_PREFIX_OPTION_LEN / 8));
- prefixOptionPacket.putInt(
- ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET,
- PREFIX_PREFERRED_LIFETIME);
- prefixOptionPacket.putInt(
- ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET,
- PREFIX_VALID_LIFETIME);
- verifyRaLifetime(
- apfFilter, ipClientCallback, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME);
- verifyRaEvent(new RaEvent(
- ROUTER_LIFETIME, PREFIX_VALID_LIFETIME, PREFIX_PREFERRED_LIFETIME, -1, -1, -1));
-
- ByteBuffer rdnssOptionPacket = ByteBuffer.wrap(
- new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
- basePacket.clear();
- rdnssOptionPacket.put(basePacket);
- rdnssOptionPacket.put((byte)ICMP6_RDNSS_OPTION_TYPE);
- rdnssOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
- rdnssOptionPacket.putInt(
- ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, RDNSS_LIFETIME);
- verifyRaLifetime(apfFilter, ipClientCallback, rdnssOptionPacket, RDNSS_LIFETIME);
- verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, RDNSS_LIFETIME, -1));
-
- ByteBuffer routeInfoOptionPacket = ByteBuffer.wrap(
- new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
- basePacket.clear();
- routeInfoOptionPacket.put(basePacket);
- routeInfoOptionPacket.put((byte)ICMP6_ROUTE_INFO_OPTION_TYPE);
- routeInfoOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
- routeInfoOptionPacket.putInt(
- ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, ROUTE_LIFETIME);
- verifyRaLifetime(apfFilter, ipClientCallback, routeInfoOptionPacket, ROUTE_LIFETIME);
- verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, ROUTE_LIFETIME, -1, -1));
-
- ByteBuffer dnsslOptionPacket = ByteBuffer.wrap(
- new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]);
- basePacket.clear();
- dnsslOptionPacket.put(basePacket);
- dnsslOptionPacket.put((byte)ICMP6_DNSSL_OPTION_TYPE);
- dnsslOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
- dnsslOptionPacket.putInt(
- ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, DNSSL_LIFETIME);
- verifyRaLifetime(apfFilter, ipClientCallback, dnsslOptionPacket, ROUTER_LIFETIME);
- verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, DNSSL_LIFETIME));
-
- // Verify that current program filters all five RAs:
- program = ipClientCallback.getApfProgram();
- verifyRaLifetime(program, basePacket, ROUTER_LIFETIME);
- verifyRaLifetime(program, newFlowLabelPacket, ROUTER_LIFETIME);
- verifyRaLifetime(program, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME);
- verifyRaLifetime(program, rdnssOptionPacket, RDNSS_LIFETIME);
- verifyRaLifetime(program, routeInfoOptionPacket, ROUTE_LIFETIME);
- verifyRaLifetime(program, dnsslOptionPacket, ROUTER_LIFETIME);
-
- apfFilter.shutdown();
- }
-
- /**
- * Stage a file for testing, i.e. make it native accessible. Given a resource ID,
- * copy that resource into the app's data directory and return the path to it.
- */
- private String stageFile(int rawId) throws Exception {
- File file = new File(InstrumentationRegistry.getContext().getFilesDir(), "staged_file");
- new File(file.getParent()).mkdirs();
- InputStream in = null;
- OutputStream out = null;
- try {
- in = InstrumentationRegistry.getContext().getResources().openRawResource(rawId);
- out = new FileOutputStream(file);
- Streams.copy(in, out);
- } finally {
- if (in != null) in.close();
- if (out != null) out.close();
- }
- return file.getAbsolutePath();
- }
-
- private static void put(ByteBuffer buffer, int position, byte[] bytes) {
- final int original = buffer.position();
- buffer.position(position);
- buffer.put(bytes);
- buffer.position(original);
- }
-
- @Test
- public void testRaParsing() throws Exception {
- final int maxRandomPacketSize = 512;
- final Random r = new Random();
- MockIpClientCallback cb = new MockIpClientCallback();
- ApfConfiguration config = getDefaultConfig();
- config.multicastFilter = DROP_MULTICAST;
- config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog);
- for (int i = 0; i < 1000; i++) {
- byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
- r.nextBytes(packet);
- try {
- apfFilter.new Ra(packet, packet.length);
- } catch (ApfFilter.InvalidRaException e) {
- } catch (Exception e) {
- throw new Exception("bad packet: " + HexDump.toHexString(packet), e);
- }
- }
- }
-
- @Test
- public void testRaProcessing() throws Exception {
- final int maxRandomPacketSize = 512;
- final Random r = new Random();
- MockIpClientCallback cb = new MockIpClientCallback();
- ApfConfiguration config = getDefaultConfig();
- config.multicastFilter = DROP_MULTICAST;
- config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog);
- for (int i = 0; i < 1000; i++) {
- byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
- r.nextBytes(packet);
- try {
- apfFilter.processRa(packet, packet.length);
- } catch (Exception e) {
- throw new Exception("bad packet: " + HexDump.toHexString(packet), e);
- }
- }
- }
-
- /**
- * Call the APF interpreter to run {@code program} on {@code packet} with persistent memory
- * segment {@data} pretending the filter was installed {@code filter_age} seconds ago.
- */
- private native static int apfSimulate(byte[] program, byte[] packet, byte[] data,
- int filter_age);
-
- /**
- * Compile a tcpdump human-readable filter (e.g. "icmp" or "tcp port 54") into a BPF
- * prorgam and return a human-readable dump of the BPF program identical to "tcpdump -d".
- */
- private native static String compileToBpf(String filter);
-
- /**
- * Open packet capture file {@code pcap_filename} and filter the packets using tcpdump
- * human-readable filter (e.g. "icmp" or "tcp port 54") compiled to a BPF program and
- * at the same time using APF program {@code apf_program}. Return {@code true} if
- * both APF and BPF programs filter out exactly the same packets.
- */
- private native static boolean compareBpfApf(String filter, String pcap_filename,
- byte[] apf_program);
-
-
- /**
- * Open packet capture file {@code pcapFilename} and run it through APF filter. Then
- * checks whether all the packets are dropped and populates data[] {@code data} with
- * the APF counters.
- */
- private native static boolean dropsAllPackets(byte[] program, byte[] data, String pcapFilename);
-
- @Test
- public void testBroadcastAddress() throws Exception {
- assertEqualsIp("255.255.255.255", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 0));
- assertEqualsIp("0.0.0.0", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 32));
- assertEqualsIp("0.0.3.255", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 22));
- assertEqualsIp("0.255.255.255", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 8));
-
- assertEqualsIp("255.255.255.255", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 0));
- assertEqualsIp("10.0.0.1", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 32));
- assertEqualsIp("10.0.0.255", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 24));
- assertEqualsIp("10.0.255.255", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 16));
- }
-
- public void assertEqualsIp(String expected, int got) throws Exception {
- int want = bytesToBEInt(InetAddress.getByName(expected).getAddress());
- assertEquals(want, got);
- }
-}
diff --git a/packages/NetworkStack/tests/unit/src/android/net/apf/Bpf2Apf.java b/packages/NetworkStack/tests/unit/src/android/net/apf/Bpf2Apf.java
deleted file mode 100644
index 5d57cde..0000000
--- a/packages/NetworkStack/tests/unit/src/android/net/apf/Bpf2Apf.java
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.apf;
-
-import android.net.apf.ApfGenerator;
-import android.net.apf.ApfGenerator.IllegalInstructionException;
-import android.net.apf.ApfGenerator.Register;
-
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-
-/**
- * BPF to APF translator.
- *
- * Note: This is for testing purposes only and is not guaranteed to support
- * translation of all BPF programs.
- *
- * Example usage:
- * javac net/java/android/net/apf/ApfGenerator.java \
- * tests/servicestests/src/android/net/apf/Bpf2Apf.java
- * sudo tcpdump -i em1 -d icmp | java -classpath tests/servicestests/src:net/java \
- * android.net.apf.Bpf2Apf
- */
-public class Bpf2Apf {
- private static int parseImm(String line, String arg) {
- if (!arg.startsWith("#0x")) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- final long val_long = Long.parseLong(arg.substring(3), 16);
- if (val_long < 0 || val_long > Long.parseLong("ffffffff", 16)) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- return new Long((val_long << 32) >> 32).intValue();
- }
-
- /**
- * Convert a single line of "tcpdump -d" (human readable BPF program dump) {@code line} into
- * APF instruction(s) and append them to {@code gen}. Here's an example line:
- * (001) jeq #0x86dd jt 2 jf 7
- */
- private static void convertLine(String line, ApfGenerator gen)
- throws IllegalInstructionException {
- if (line.indexOf("(") != 0 || line.indexOf(")") != 4 || line.indexOf(" ") != 5) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- int label = Integer.parseInt(line.substring(1, 4));
- gen.defineLabel(Integer.toString(label));
- String opcode = line.substring(6, 10).trim();
- String arg = line.substring(15, Math.min(32, line.length())).trim();
- switch (opcode) {
- case "ld":
- case "ldh":
- case "ldb":
- case "ldx":
- case "ldxb":
- case "ldxh":
- Register dest = opcode.contains("x") ? Register.R1 : Register.R0;
- if (arg.equals("4*([14]&0xf)")) {
- if (!opcode.equals("ldxb")) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- gen.addLoadFromMemory(dest, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
- break;
- }
- if (arg.equals("#pktlen")) {
- if (!opcode.equals("ld")) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- gen.addLoadFromMemory(dest, gen.PACKET_SIZE_MEMORY_SLOT);
- break;
- }
- if (arg.startsWith("#0x")) {
- if (!opcode.equals("ld")) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- gen.addLoadImmediate(dest, parseImm(line, arg));
- break;
- }
- if (arg.startsWith("M[")) {
- if (!opcode.startsWith("ld")) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- int memory_slot = Integer.parseInt(arg.substring(2, arg.length() - 1));
- if (memory_slot < 0 || memory_slot >= gen.MEMORY_SLOTS ||
- // Disallow use of pre-filled slots as BPF programs might
- // wrongfully assume they're initialized to 0.
- (memory_slot >= gen.FIRST_PREFILLED_MEMORY_SLOT &&
- memory_slot <= gen.LAST_PREFILLED_MEMORY_SLOT)) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- gen.addLoadFromMemory(dest, memory_slot);
- break;
- }
- if (arg.startsWith("[x + ")) {
- int offset = Integer.parseInt(arg.substring(5, arg.length() - 1));
- switch (opcode) {
- case "ld":
- case "ldx":
- gen.addLoad32Indexed(dest, offset);
- break;
- case "ldh":
- case "ldxh":
- gen.addLoad16Indexed(dest, offset);
- break;
- case "ldb":
- case "ldxb":
- gen.addLoad8Indexed(dest, offset);
- break;
- }
- } else {
- int offset = Integer.parseInt(arg.substring(1, arg.length() - 1));
- switch (opcode) {
- case "ld":
- case "ldx":
- gen.addLoad32(dest, offset);
- break;
- case "ldh":
- case "ldxh":
- gen.addLoad16(dest, offset);
- break;
- case "ldb":
- case "ldxb":
- gen.addLoad8(dest, offset);
- break;
- }
- }
- break;
- case "st":
- case "stx":
- Register src = opcode.contains("x") ? Register.R1 : Register.R0;
- if (!arg.startsWith("M[")) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- int memory_slot = Integer.parseInt(arg.substring(2, arg.length() - 1));
- if (memory_slot < 0 || memory_slot >= gen.MEMORY_SLOTS ||
- // Disallow overwriting pre-filled slots
- (memory_slot >= gen.FIRST_PREFILLED_MEMORY_SLOT &&
- memory_slot <= gen.LAST_PREFILLED_MEMORY_SLOT)) {
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- gen.addStoreToMemory(src, memory_slot);
- break;
- case "add":
- case "and":
- case "or":
- case "sub":
- if (arg.equals("x")) {
- switch(opcode) {
- case "add":
- gen.addAddR1();
- break;
- case "and":
- gen.addAndR1();
- break;
- case "or":
- gen.addOrR1();
- break;
- case "sub":
- gen.addNeg(Register.R1);
- gen.addAddR1();
- gen.addNeg(Register.R1);
- break;
- }
- } else {
- int imm = parseImm(line, arg);
- switch(opcode) {
- case "add":
- gen.addAdd(imm);
- break;
- case "and":
- gen.addAnd(imm);
- break;
- case "or":
- gen.addOr(imm);
- break;
- case "sub":
- gen.addAdd(-imm);
- break;
- }
- }
- break;
- case "jeq":
- case "jset":
- case "jgt":
- case "jge":
- int val = 0;
- boolean reg_compare;
- if (arg.startsWith("x")) {
- reg_compare = true;
- } else {
- reg_compare = false;
- val = parseImm(line, arg);
- }
- int jt_offset = line.indexOf("jt");
- int jf_offset = line.indexOf("jf");
- String true_label = line.substring(jt_offset + 2, jf_offset).trim();
- String false_label = line.substring(jf_offset + 2).trim();
- boolean true_label_is_fallthrough = Integer.parseInt(true_label) == label + 1;
- boolean false_label_is_fallthrough = Integer.parseInt(false_label) == label + 1;
- if (true_label_is_fallthrough && false_label_is_fallthrough)
- break;
- switch (opcode) {
- case "jeq":
- if (!true_label_is_fallthrough) {
- if (reg_compare) {
- gen.addJumpIfR0EqualsR1(true_label);
- } else {
- gen.addJumpIfR0Equals(val, true_label);
- }
- }
- if (!false_label_is_fallthrough) {
- if (!true_label_is_fallthrough) {
- gen.addJump(false_label);
- } else if (reg_compare) {
- gen.addJumpIfR0NotEqualsR1(false_label);
- } else {
- gen.addJumpIfR0NotEquals(val, false_label);
- }
- }
- break;
- case "jset":
- if (reg_compare) {
- gen.addJumpIfR0AnyBitsSetR1(true_label);
- } else {
- gen.addJumpIfR0AnyBitsSet(val, true_label);
- }
- if (!false_label_is_fallthrough) {
- gen.addJump(false_label);
- }
- break;
- case "jgt":
- if (!true_label_is_fallthrough ||
- // We have no less-than-or-equal-to register to register
- // comparison instruction, so in this case we'll jump
- // around an unconditional jump.
- (!false_label_is_fallthrough && reg_compare)) {
- if (reg_compare) {
- gen.addJumpIfR0GreaterThanR1(true_label);
- } else {
- gen.addJumpIfR0GreaterThan(val, true_label);
- }
- }
- if (!false_label_is_fallthrough) {
- if (!true_label_is_fallthrough || reg_compare) {
- gen.addJump(false_label);
- } else {
- gen.addJumpIfR0LessThan(val + 1, false_label);
- }
- }
- break;
- case "jge":
- if (!false_label_is_fallthrough ||
- // We have no greater-than-or-equal-to register to register
- // comparison instruction, so in this case we'll jump
- // around an unconditional jump.
- (!true_label_is_fallthrough && reg_compare)) {
- if (reg_compare) {
- gen.addJumpIfR0LessThanR1(false_label);
- } else {
- gen.addJumpIfR0LessThan(val, false_label);
- }
- }
- if (!true_label_is_fallthrough) {
- if (!false_label_is_fallthrough || reg_compare) {
- gen.addJump(true_label);
- } else {
- gen.addJumpIfR0GreaterThan(val - 1, true_label);
- }
- }
- break;
- }
- break;
- case "ret":
- if (arg.equals("#0")) {
- gen.addJump(gen.DROP_LABEL);
- } else {
- gen.addJump(gen.PASS_LABEL);
- }
- break;
- case "tax":
- gen.addMove(Register.R1);
- break;
- case "txa":
- gen.addMove(Register.R0);
- break;
- default:
- throw new IllegalArgumentException("Unhandled instruction: " + line);
- }
- }
-
- /**
- * Convert the output of "tcpdump -d" (human readable BPF program dump) {@code bpf} into an APF
- * program and return it.
- */
- public static byte[] convert(String bpf) throws IllegalInstructionException {
- ApfGenerator gen = new ApfGenerator(3);
- for (String line : bpf.split("\\n")) convertLine(line, gen);
- return gen.generate();
- }
-
- /**
- * Convert the output of "tcpdump -d" (human readable BPF program dump) piped in stdin into an
- * APF program and output it via stdout.
- */
- public static void main(String[] args) throws Exception {
- BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
- String line = null;
- StringBuilder responseData = new StringBuilder();
- ApfGenerator gen = new ApfGenerator(3);
- while ((line = in.readLine()) != null) convertLine(line, gen);
- System.out.write(gen.generate());
- }
-}
diff --git a/packages/NetworkStack/tests/unit/src/android/net/captiveportal/CaptivePortalProbeSpecTest.java b/packages/NetworkStack/tests/unit/src/android/net/captiveportal/CaptivePortalProbeSpecTest.java
deleted file mode 100644
index f948086..0000000
--- a/packages/NetworkStack/tests/unit/src/android/net/captiveportal/CaptivePortalProbeSpecTest.java
+++ /dev/null
@@ -1,170 +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 android.net.captiveportal;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.net.MalformedURLException;
-import java.text.ParseException;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class CaptivePortalProbeSpecTest {
-
- @Test
- public void testGetResult_Regex() throws MalformedURLException, ParseException {
- // 2xx status or 404, with an empty (match everything) location regex
- CaptivePortalProbeSpec statusRegexSpec = CaptivePortalProbeSpec.parseSpec(
- "http://www.google.com@@/@@2[0-9]{2}|404@@/@@");
-
- // 404, or 301/302 redirect to some HTTPS page under google.com
- CaptivePortalProbeSpec redirectSpec = CaptivePortalProbeSpec.parseSpec(
- "http://google.com@@/@@404|30[12]@@/@@https://([0-9a-z]+\\.)*google\\.com.*");
-
- assertSuccess(statusRegexSpec.getResult(200, null));
- assertSuccess(statusRegexSpec.getResult(299, "qwer"));
- assertSuccess(statusRegexSpec.getResult(404, null));
- assertSuccess(statusRegexSpec.getResult(404, ""));
-
- assertPortal(statusRegexSpec.getResult(300, null));
- assertPortal(statusRegexSpec.getResult(399, "qwer"));
- assertPortal(statusRegexSpec.getResult(500, null));
-
- assertSuccess(redirectSpec.getResult(404, null));
- assertSuccess(redirectSpec.getResult(404, ""));
- assertSuccess(redirectSpec.getResult(301, "https://www.google.com"));
- assertSuccess(redirectSpec.getResult(301, "https://www.google.com/test?q=3"));
- assertSuccess(redirectSpec.getResult(302, "https://google.com/test?q=3"));
-
- assertPortal(redirectSpec.getResult(299, "https://google.com/test?q=3"));
- assertPortal(redirectSpec.getResult(299, ""));
- assertPortal(redirectSpec.getResult(499, null));
- assertPortal(redirectSpec.getResult(301, "http://login.portal.example.com/loginpage"));
- assertPortal(redirectSpec.getResult(302, "http://www.google.com/test?q=3"));
- }
-
- @Test(expected = ParseException.class)
- public void testParseSpec_Empty() throws MalformedURLException, ParseException {
- CaptivePortalProbeSpec.parseSpec("");
- }
-
- @Test(expected = ParseException.class)
- public void testParseSpec_Null() throws MalformedURLException, ParseException {
- CaptivePortalProbeSpec.parseSpec(null);
- }
-
- @Test(expected = ParseException.class)
- public void testParseSpec_MissingParts() throws MalformedURLException, ParseException {
- CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@123");
- }
-
- @Test(expected = ParseException.class)
- public void testParseSpec_TooManyParts() throws MalformedURLException, ParseException {
- CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@123@@/@@456@@/@@extra");
- }
-
- @Test(expected = ParseException.class)
- public void testParseSpec_InvalidStatusRegex() throws MalformedURLException, ParseException {
- CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@unmatched(parenthesis@@/@@456");
- }
-
- @Test(expected = ParseException.class)
- public void testParseSpec_InvalidLocationRegex() throws MalformedURLException, ParseException {
- CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@123@@/@@unmatched[[]bracket");
- }
-
- @Test(expected = MalformedURLException.class)
- public void testParseSpec_EmptyURL() throws MalformedURLException, ParseException {
- CaptivePortalProbeSpec.parseSpec("@@/@@123@@/@@123");
- }
-
- @Test(expected = ParseException.class)
- public void testParseSpec_NoParts() throws MalformedURLException, ParseException {
- CaptivePortalProbeSpec.parseSpec("invalid");
- }
-
- @Test(expected = MalformedURLException.class)
- public void testParseSpec_RegexInvalidUrl() throws MalformedURLException, ParseException {
- CaptivePortalProbeSpec.parseSpec("notaurl@@/@@123@@/@@123");
- }
-
- @Test
- public void testParseSpecOrNull_UsesSpec() {
- final String specUrl = "http://google.com/probe";
- final String redirectUrl = "https://google.com/probe";
- CaptivePortalProbeSpec spec = CaptivePortalProbeSpec.parseSpecOrNull(
- specUrl + "@@/@@302@@/@@" + redirectUrl);
- assertEquals(specUrl, spec.getUrl().toString());
-
- assertPortal(spec.getResult(302, "http://portal.example.com"));
- assertSuccess(spec.getResult(302, redirectUrl));
- }
-
- @Test
- public void testParseSpecOrNull_UsesFallback() throws MalformedURLException {
- CaptivePortalProbeSpec spec = CaptivePortalProbeSpec.parseSpecOrNull(null);
- assertNull(spec);
-
- spec = CaptivePortalProbeSpec.parseSpecOrNull("");
- assertNull(spec);
-
- spec = CaptivePortalProbeSpec.parseSpecOrNull("@@/@@ @@/@@ @@/@@");
- assertNull(spec);
-
- spec = CaptivePortalProbeSpec.parseSpecOrNull("invalid@@/@@123@@/@@456");
- assertNull(spec);
- }
-
- @Test
- public void testParseSpecOrUseStatusCodeFallback_EmptySpec() throws MalformedURLException {
- CaptivePortalProbeSpec spec = CaptivePortalProbeSpec.parseSpecOrNull("");
- assertNull(spec);
- }
-
- private void assertIsStatusSpec(CaptivePortalProbeSpec spec) {
- assertSuccess(spec.getResult(204, null));
- assertSuccess(spec.getResult(204, "1234"));
-
- assertPortal(spec.getResult(200, null));
- assertPortal(spec.getResult(301, null));
- assertPortal(spec.getResult(302, "1234"));
- assertPortal(spec.getResult(399, ""));
-
- assertFailed(spec.getResult(404, null));
- assertFailed(spec.getResult(500, "1234"));
- }
-
- private void assertPortal(CaptivePortalProbeResult result) {
- assertTrue(result.isPortal());
- }
-
- private void assertSuccess(CaptivePortalProbeResult result) {
- assertTrue(result.isSuccessful());
- }
-
- private void assertFailed(CaptivePortalProbeResult result) {
- assertTrue(result.isFailed());
- }
-}
diff --git a/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpLeaseRepositoryTest.java b/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
deleted file mode 100644
index 27d7255..0000000
--- a/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
+++ /dev/null
@@ -1,544 +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 android.net.dhcp;
-
-import static android.net.InetAddresses.parseNumericAddress;
-import static android.net.dhcp.DhcpLease.HOSTNAME_NONE;
-import static android.net.dhcp.DhcpLeaseRepository.CLIENTID_UNSPEC;
-import static android.net.dhcp.DhcpLeaseRepository.INETADDR_UNSPEC;
-
-import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.when;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.IpPrefix;
-import android.net.MacAddress;
-import android.net.dhcp.DhcpServer.Clock;
-import android.net.util.SharedLog;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import static java.lang.String.format;
-
-import java.net.Inet4Address;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class DhcpLeaseRepositoryTest {
- private static final Inet4Address TEST_DEF_ROUTER = parseAddr4("192.168.42.247");
- private static final Inet4Address TEST_SERVER_ADDR = parseAddr4("192.168.42.241");
- private static final Inet4Address TEST_RESERVED_ADDR = parseAddr4("192.168.42.243");
- private static final MacAddress TEST_MAC_1 = MacAddress.fromBytes(
- new byte[] { 5, 4, 3, 2, 1, 0 });
- private static final MacAddress TEST_MAC_2 = MacAddress.fromBytes(
- new byte[] { 0, 1, 2, 3, 4, 5 });
- private static final MacAddress TEST_MAC_3 = MacAddress.fromBytes(
- new byte[] { 0, 1, 2, 3, 4, 6 });
- private static final Inet4Address TEST_INETADDR_1 = parseAddr4("192.168.42.248");
- private static final Inet4Address TEST_INETADDR_2 = parseAddr4("192.168.42.249");
- private static final String TEST_HOSTNAME_1 = "hostname1";
- private static final String TEST_HOSTNAME_2 = "hostname2";
- private static final IpPrefix TEST_IP_PREFIX = new IpPrefix(TEST_SERVER_ADDR, 22);
- private static final long TEST_TIME = 100L;
- private static final int TEST_LEASE_TIME_MS = 3_600_000;
- private static final Set<Inet4Address> TEST_EXCL_SET =
- Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
- TEST_SERVER_ADDR, TEST_DEF_ROUTER, TEST_RESERVED_ADDR)));
-
- @NonNull
- private SharedLog mLog;
- @NonNull @Mock
- private Clock mClock;
- @NonNull
- private DhcpLeaseRepository mRepo;
-
- private static Inet4Address parseAddr4(String inet4Addr) {
- return (Inet4Address) parseNumericAddress(inet4Addr);
- }
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mLog = new SharedLog("DhcpLeaseRepositoryTest");
- when(mClock.elapsedRealtime()).thenReturn(TEST_TIME);
- mRepo = new DhcpLeaseRepository(
- TEST_IP_PREFIX, TEST_EXCL_SET, TEST_LEASE_TIME_MS, mLog, mClock);
- }
-
- /**
- * Request a number of addresses through offer/request. Useful to test address exhaustion.
- * @param nAddr Number of addresses to request.
- */
- private void requestAddresses(byte nAddr) throws Exception {
- final HashSet<Inet4Address> addrs = new HashSet<>();
- byte[] hwAddrBytes = new byte[] { 8, 4, 3, 2, 1, 0 };
- for (byte i = 0; i < nAddr; i++) {
- hwAddrBytes[5] = i;
- MacAddress newMac = MacAddress.fromBytes(hwAddrBytes);
- final String hostname = "host_" + i;
- final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, newMac,
- IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, hostname);
-
- assertNotNull(lease);
- assertEquals(newMac, lease.getHwAddr());
- assertEquals(hostname, lease.getHostname());
- assertTrue(format("Duplicate address allocated: %s in %s", lease.getNetAddr(), addrs),
- addrs.add(lease.getNetAddr()));
-
- requestLeaseSelecting(newMac, lease.getNetAddr(), hostname);
- }
- }
-
- @Test
- public void testAddressExhaustion() throws Exception {
- // Use a /28 to quickly run out of addresses
- mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS);
-
- // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses
- requestAddresses((byte) 11);
-
- try {
- mRepo.getOffer(null, TEST_MAC_2,
- IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
- fail("Should be out of addresses");
- } catch (DhcpLeaseRepository.OutOfAddressesException e) {
- // Expected
- }
- }
-
- @Test
- public void testUpdateParams_LeaseCleanup() throws Exception {
- // Inside /28:
- final Inet4Address reqAddrIn28 = parseAddr4("192.168.42.242");
- final Inet4Address declinedAddrIn28 = parseAddr4("192.168.42.245");
-
- // Inside /28, but not available there (first address of the range)
- final Inet4Address declinedFirstAddrIn28 = parseAddr4("192.168.42.240");
-
- final DhcpLease reqAddrIn28Lease = requestLeaseSelecting(TEST_MAC_1, reqAddrIn28);
- mRepo.markLeaseDeclined(declinedAddrIn28);
- mRepo.markLeaseDeclined(declinedFirstAddrIn28);
-
- // Inside /22, but outside /28:
- final Inet4Address reqAddrIn22 = parseAddr4("192.168.42.3");
- final Inet4Address declinedAddrIn22 = parseAddr4("192.168.42.4");
-
- final DhcpLease reqAddrIn22Lease = requestLeaseSelecting(TEST_MAC_3, reqAddrIn22);
- mRepo.markLeaseDeclined(declinedAddrIn22);
-
- // Address that will be reserved in the updateParams call below
- final Inet4Address reservedAddr = parseAddr4("192.168.42.244");
- final DhcpLease reservedAddrLease = requestLeaseSelecting(TEST_MAC_2, reservedAddr);
-
- // Update from /22 to /28 and add another reserved address
- Set<Inet4Address> newReserved = new HashSet<>(TEST_EXCL_SET);
- newReserved.add(reservedAddr);
- mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), newReserved, TEST_LEASE_TIME_MS);
-
- assertHasLease(reqAddrIn28Lease);
- assertDeclined(declinedAddrIn28);
-
- assertNotDeclined(declinedFirstAddrIn28);
-
- assertNoLease(reqAddrIn22Lease);
- assertNotDeclined(declinedAddrIn22);
-
- assertNoLease(reservedAddrLease);
- }
-
- @Test
- public void testGetOffer_StableAddress() throws Exception {
- for (final MacAddress macAddr : new MacAddress[] { TEST_MAC_1, TEST_MAC_2, TEST_MAC_3 }) {
- final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, macAddr,
- IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
-
- // Same lease is offered twice
- final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, macAddr,
- IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
- assertEquals(lease, newLease);
- }
- }
-
- @Test
- public void testUpdateParams_UsesNewPrefix() throws Exception {
- final IpPrefix newPrefix = new IpPrefix(parseAddr4("192.168.123.0"), 24);
- mRepo.updateParams(newPrefix, TEST_EXCL_SET, TEST_LEASE_TIME_MS);
-
- DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
- IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
- assertTrue(newPrefix.contains(lease.getNetAddr()));
- }
-
- @Test
- public void testGetOffer_ExistingLease() throws Exception {
- requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1, TEST_HOSTNAME_1);
-
- DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
- IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
- assertEquals(TEST_INETADDR_1, offer.getNetAddr());
- assertEquals(TEST_HOSTNAME_1, offer.getHostname());
- }
-
- @Test
- public void testGetOffer_ClientIdHasExistingLease() throws Exception {
- final byte[] clientId = new byte[] { 1, 2 };
- mRepo.requestLease(clientId, TEST_MAC_1, IPV4_ADDR_ANY /* clientAddr */,
- IPV4_ADDR_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, false,
- TEST_HOSTNAME_1);
-
- // Different MAC, but same clientId
- DhcpLease offer = mRepo.getOffer(clientId, TEST_MAC_2,
- IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
- assertEquals(TEST_INETADDR_1, offer.getNetAddr());
- assertEquals(TEST_HOSTNAME_1, offer.getHostname());
- }
-
- @Test
- public void testGetOffer_DifferentClientId() throws Exception {
- final byte[] clientId1 = new byte[] { 1, 2 };
- final byte[] clientId2 = new byte[] { 3, 4 };
- mRepo.requestLease(clientId1, TEST_MAC_1, IPV4_ADDR_ANY /* clientAddr */,
- IPV4_ADDR_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, false,
- TEST_HOSTNAME_1);
-
- // Same MAC, different client ID
- DhcpLease offer = mRepo.getOffer(clientId2, TEST_MAC_1,
- IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
- // Obtains a different address
- assertNotEquals(TEST_INETADDR_1, offer.getNetAddr());
- assertEquals(HOSTNAME_NONE, offer.getHostname());
- assertEquals(TEST_MAC_1, offer.getHwAddr());
- }
-
- @Test
- public void testGetOffer_RequestedAddress() throws Exception {
- DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */,
- TEST_INETADDR_1 /* reqAddr */, TEST_HOSTNAME_1);
- assertEquals(TEST_INETADDR_1, offer.getNetAddr());
- assertEquals(TEST_HOSTNAME_1, offer.getHostname());
- }
-
- @Test
- public void testGetOffer_RequestedAddressInUse() throws Exception {
- requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
- DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2, IPV4_ADDR_ANY /* relayAddr */,
- TEST_INETADDR_1 /* reqAddr */, HOSTNAME_NONE);
- assertNotEquals(TEST_INETADDR_1, offer.getNetAddr());
- }
-
- @Test
- public void testGetOffer_RequestedAddressReserved() throws Exception {
- DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */,
- TEST_RESERVED_ADDR /* reqAddr */, HOSTNAME_NONE);
- assertNotEquals(TEST_RESERVED_ADDR, offer.getNetAddr());
- }
-
- @Test
- public void testGetOffer_RequestedAddressInvalid() throws Exception {
- final Inet4Address invalidAddr = parseAddr4("192.168.42.0");
- DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */,
- invalidAddr /* reqAddr */, HOSTNAME_NONE);
- assertNotEquals(invalidAddr, offer.getNetAddr());
- }
-
- @Test
- public void testGetOffer_RequestedAddressOutsideSubnet() throws Exception {
- final Inet4Address invalidAddr = parseAddr4("192.168.254.2");
- DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */,
- invalidAddr /* reqAddr */, HOSTNAME_NONE);
- assertNotEquals(invalidAddr, offer.getNetAddr());
- }
-
- @Test(expected = DhcpLeaseRepository.InvalidSubnetException.class)
- public void testGetOffer_RelayInInvalidSubnet() throws Exception {
- mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, parseAddr4("192.168.254.2") /* relayAddr */,
- INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
- }
-
- @Test
- public void testRequestLease_SelectingTwice() throws Exception {
- final DhcpLease lease1 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1,
- TEST_HOSTNAME_1);
-
- // Second request from same client for a different address
- final DhcpLease lease2 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_2,
- TEST_HOSTNAME_2);
-
- assertEquals(TEST_INETADDR_1, lease1.getNetAddr());
- assertEquals(TEST_HOSTNAME_1, lease1.getHostname());
-
- assertEquals(TEST_INETADDR_2, lease2.getNetAddr());
- assertEquals(TEST_HOSTNAME_2, lease2.getHostname());
-
- // First address freed when client requested a different one: another client can request it
- final DhcpLease lease3 = requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1, HOSTNAME_NONE);
- assertEquals(TEST_INETADDR_1, lease3.getNetAddr());
- }
-
- @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
- public void testRequestLease_SelectingInvalid() throws Exception {
- requestLeaseSelecting(TEST_MAC_1, parseAddr4("192.168.254.5"));
- }
-
- @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
- public void testRequestLease_SelectingInUse() throws Exception {
- requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
- requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1);
- }
-
- @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
- public void testRequestLease_SelectingReserved() throws Exception {
- requestLeaseSelecting(TEST_MAC_1, TEST_RESERVED_ADDR);
- }
-
- @Test(expected = DhcpLeaseRepository.InvalidSubnetException.class)
- public void testRequestLease_SelectingRelayInInvalidSubnet() throws Exception {
- mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* clientAddr */,
- parseAddr4("192.168.128.1") /* relayAddr */, TEST_INETADDR_1 /* reqAddr */,
- true /* sidSet */, HOSTNAME_NONE);
- }
-
- @Test
- public void testRequestLease_InitReboot() throws Exception {
- // Request address once
- requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
-
- final long newTime = TEST_TIME + 100;
- when(mClock.elapsedRealtime()).thenReturn(newTime);
-
- // init-reboot (sidSet == false): verify configuration
- final DhcpLease lease = requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_1);
- assertEquals(TEST_INETADDR_1, lease.getNetAddr());
- assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime());
- }
-
- @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
- public void testRequestLease_InitRebootWrongAddr() throws Exception {
- // Request address once
- requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
- // init-reboot with different requested address
- requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_2);
- }
-
- @Test
- public void testRequestLease_InitRebootUnknownAddr() throws Exception {
- // init-reboot with unknown requested address
- final DhcpLease lease = requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_2);
- // RFC2131 says we should not reply to accommodate other servers, but since we are
- // authoritative we allow creating the lease to avoid issues with lost lease DB (same as
- // dnsmasq behavior)
- assertEquals(TEST_INETADDR_2, lease.getNetAddr());
- }
-
- @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
- public void testRequestLease_InitRebootWrongSubnet() throws Exception {
- requestLeaseInitReboot(TEST_MAC_1, parseAddr4("192.168.254.2"));
- }
-
- @Test
- public void testRequestLease_Renewing() throws Exception {
- requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
-
- final long newTime = TEST_TIME + 100;
- when(mClock.elapsedRealtime()).thenReturn(newTime);
-
- final DhcpLease lease = requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1);
-
- assertEquals(TEST_INETADDR_1, lease.getNetAddr());
- assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime());
- }
-
- @Test
- public void testRequestLease_RenewingUnknownAddr() throws Exception {
- final long newTime = TEST_TIME + 100;
- when(mClock.elapsedRealtime()).thenReturn(newTime);
- final DhcpLease lease = requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1);
- // Allows renewing an unknown address if available
- assertEquals(TEST_INETADDR_1, lease.getNetAddr());
- assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime());
- }
-
- @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
- public void testRequestLease_RenewingAddrInUse() throws Exception {
- requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1);
- requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1);
- }
-
- @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
- public void testRequestLease_RenewingInvalidAddr() throws Exception {
- requestLeaseRenewing(TEST_MAC_1, parseAddr4("192.168.254.2"));
- }
-
- @Test
- public void testReleaseLease() throws Exception {
- final DhcpLease lease1 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
-
- assertHasLease(lease1);
- assertTrue(mRepo.releaseLease(CLIENTID_UNSPEC, TEST_MAC_1, TEST_INETADDR_1));
- assertNoLease(lease1);
-
- final DhcpLease lease2 = requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1);
- assertEquals(TEST_INETADDR_1, lease2.getNetAddr());
- }
-
- @Test
- public void testReleaseLease_UnknownLease() {
- assertFalse(mRepo.releaseLease(CLIENTID_UNSPEC, TEST_MAC_1, TEST_INETADDR_1));
- }
-
- @Test
- public void testReleaseLease_StableOffer() throws Exception {
- for (MacAddress mac : new MacAddress[] { TEST_MAC_1, TEST_MAC_2, TEST_MAC_3 }) {
- final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, mac,
- IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
-
- requestLeaseSelecting(mac, lease.getNetAddr());
- mRepo.releaseLease(CLIENTID_UNSPEC, mac, lease.getNetAddr());
-
- // Same lease is offered after it was released
- final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, mac,
- IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
- assertEquals(lease.getNetAddr(), newLease.getNetAddr());
- }
- }
-
- @Test
- public void testMarkLeaseDeclined() throws Exception {
- final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
- IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
-
- mRepo.markLeaseDeclined(lease.getNetAddr());
-
- // Same lease is not offered again
- final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
- IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
- assertNotEquals(lease.getNetAddr(), newLease.getNetAddr());
- }
-
- @Test
- public void testMarkLeaseDeclined_UsedIfOutOfAddresses() throws Exception {
- // Use a /28 to quickly run out of addresses
- mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS);
-
- mRepo.markLeaseDeclined(TEST_INETADDR_1);
- mRepo.markLeaseDeclined(TEST_INETADDR_2);
-
- // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses
- requestAddresses((byte) 9);
-
- // Last 2 addresses: addresses marked declined should be used
- final DhcpLease firstLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
- IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_1);
- requestLeaseSelecting(TEST_MAC_1, firstLease.getNetAddr());
-
- final DhcpLease secondLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2,
- IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_2);
- requestLeaseSelecting(TEST_MAC_2, secondLease.getNetAddr());
-
- // Now out of addresses
- try {
- mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_3, IPV4_ADDR_ANY /* relayAddr */,
- INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
- fail("Repository should be out of addresses and throw");
- } catch (DhcpLeaseRepository.OutOfAddressesException e) { /* expected */ }
-
- assertEquals(TEST_INETADDR_1, firstLease.getNetAddr());
- assertEquals(TEST_HOSTNAME_1, firstLease.getHostname());
- assertEquals(TEST_INETADDR_2, secondLease.getNetAddr());
- assertEquals(TEST_HOSTNAME_2, secondLease.getHostname());
- }
-
- private DhcpLease requestLease(@NonNull MacAddress macAddr, @NonNull Inet4Address clientAddr,
- @Nullable Inet4Address reqAddr, @Nullable String hostname, boolean sidSet)
- throws DhcpLeaseRepository.DhcpLeaseException {
- return mRepo.requestLease(CLIENTID_UNSPEC, macAddr, clientAddr,
- IPV4_ADDR_ANY /* relayAddr */,
- reqAddr, sidSet, hostname);
- }
-
- /**
- * Request a lease simulating a client in the SELECTING state.
- */
- private DhcpLease requestLeaseSelecting(@NonNull MacAddress macAddr,
- @NonNull Inet4Address reqAddr, @Nullable String hostname)
- throws DhcpLeaseRepository.DhcpLeaseException {
- return requestLease(macAddr, IPV4_ADDR_ANY /* clientAddr */, reqAddr, hostname,
- true /* sidSet */);
- }
-
- /**
- * Request a lease simulating a client in the SELECTING state.
- */
- private DhcpLease requestLeaseSelecting(@NonNull MacAddress macAddr,
- @NonNull Inet4Address reqAddr) throws DhcpLeaseRepository.DhcpLeaseException {
- return requestLeaseSelecting(macAddr, reqAddr, HOSTNAME_NONE);
- }
-
- /**
- * Request a lease simulating a client in the INIT-REBOOT state.
- */
- private DhcpLease requestLeaseInitReboot(@NonNull MacAddress macAddr,
- @NonNull Inet4Address reqAddr) throws DhcpLeaseRepository.DhcpLeaseException {
- return requestLease(macAddr, IPV4_ADDR_ANY /* clientAddr */, reqAddr, HOSTNAME_NONE,
- false /* sidSet */);
- }
-
- /**
- * Request a lease simulating a client in the RENEWING state.
- */
- private DhcpLease requestLeaseRenewing(@NonNull MacAddress macAddr,
- @NonNull Inet4Address clientAddr) throws DhcpLeaseRepository.DhcpLeaseException {
- // Renewing: clientAddr filled in, no reqAddr
- return requestLease(macAddr, clientAddr, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE,
- true /* sidSet */);
- }
-
- private void assertNoLease(DhcpLease lease) {
- assertFalse("Leases contain " + lease, mRepo.getCommittedLeases().contains(lease));
- }
-
- private void assertHasLease(DhcpLease lease) {
- assertTrue("Leases do not contain " + lease, mRepo.getCommittedLeases().contains(lease));
- }
-
- private void assertNotDeclined(Inet4Address addr) {
- assertFalse("Address is declined: " + addr, mRepo.getDeclinedAddresses().contains(addr));
- }
-
- private void assertDeclined(Inet4Address addr) {
- assertTrue("Address is not declined: " + addr, mRepo.getDeclinedAddresses().contains(addr));
- }
-}
diff --git a/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpPacketTest.java b/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpPacketTest.java
deleted file mode 100644
index a30d3e4..0000000
--- a/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpPacketTest.java
+++ /dev/null
@@ -1,1117 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.dhcp;
-
-import static android.net.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS;
-import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER;
-import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME;
-import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME;
-import static android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE_ACK;
-import static android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE_OFFER;
-import static android.net.dhcp.DhcpPacket.DHCP_MTU;
-import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME;
-import static android.net.dhcp.DhcpPacket.DHCP_RENEWAL_TIME;
-import static android.net.dhcp.DhcpPacket.DHCP_ROUTER;
-import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK;
-import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO;
-import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
-import static android.net.dhcp.DhcpPacket.ENCAP_L2;
-import static android.net.dhcp.DhcpPacket.ENCAP_L3;
-import static android.net.dhcp.DhcpPacket.INADDR_ANY;
-import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
-import static android.net.dhcp.DhcpPacket.ParseException;
-import static android.net.shared.Inet4AddressUtils.getBroadcastAddress;
-import static android.net.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.annotation.Nullable;
-import android.net.DhcpResults;
-import android.net.LinkAddress;
-import android.net.NetworkUtils;
-import android.net.metrics.DhcpErrorEvent;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.util.HexDump;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.ByteArrayOutputStream;
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Random;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class DhcpPacketTest {
-
- private static final Inet4Address SERVER_ADDR = v4Address("192.0.2.1");
- private static final Inet4Address CLIENT_ADDR = v4Address("192.0.2.234");
- private static final int PREFIX_LENGTH = 22;
- private static final Inet4Address NETMASK = getPrefixMaskAsInet4Address(PREFIX_LENGTH);
- private static final Inet4Address BROADCAST_ADDR = getBroadcastAddress(
- SERVER_ADDR, PREFIX_LENGTH);
- private static final String HOSTNAME = "testhostname";
- private static final short MTU = 1500;
- // Use our own empty address instead of IPV4_ADDR_ANY or INADDR_ANY to ensure that the code
- // doesn't use == instead of equals when comparing addresses.
- private static final Inet4Address ANY = v4Address("0.0.0.0");
-
- private static final byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
-
- private static final Inet4Address v4Address(String addrString) throws IllegalArgumentException {
- return (Inet4Address) NetworkUtils.numericToInetAddress(addrString);
- }
-
- @Before
- public void setUp() {
- DhcpPacket.testOverrideVendorId = "android-dhcp-???";
- DhcpPacket.testOverrideHostname = "android-01234567890abcde";
- }
-
- class TestDhcpPacket extends DhcpPacket {
- private byte mType;
- // TODO: Make this a map of option numbers to bytes instead.
- private byte[] mDomainBytes, mVendorInfoBytes, mLeaseTimeBytes, mNetmaskBytes;
-
- public TestDhcpPacket(byte type, Inet4Address clientIp, Inet4Address yourIp) {
- super(0xdeadbeef, (short) 0, clientIp, yourIp, INADDR_ANY, INADDR_ANY,
- CLIENT_MAC, true);
- mType = type;
- }
-
- public TestDhcpPacket(byte type) {
- this(type, INADDR_ANY, CLIENT_ADDR);
- }
-
- public TestDhcpPacket setDomainBytes(byte[] domainBytes) {
- mDomainBytes = domainBytes;
- return this;
- }
-
- public TestDhcpPacket setVendorInfoBytes(byte[] vendorInfoBytes) {
- mVendorInfoBytes = vendorInfoBytes;
- return this;
- }
-
- public TestDhcpPacket setLeaseTimeBytes(byte[] leaseTimeBytes) {
- mLeaseTimeBytes = leaseTimeBytes;
- return this;
- }
-
- public TestDhcpPacket setNetmaskBytes(byte[] netmaskBytes) {
- mNetmaskBytes = netmaskBytes;
- return this;
- }
-
- public ByteBuffer buildPacket(int encap, short unusedDestUdp, short unusedSrcUdp) {
- ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
- fillInPacket(encap, CLIENT_ADDR, SERVER_ADDR,
- DHCP_CLIENT, DHCP_SERVER, result, DHCP_BOOTREPLY, false);
- return result;
- }
-
- public void finishPacket(ByteBuffer buffer) {
- addTlv(buffer, DHCP_MESSAGE_TYPE, mType);
- if (mDomainBytes != null) {
- addTlv(buffer, DHCP_DOMAIN_NAME, mDomainBytes);
- }
- if (mVendorInfoBytes != null) {
- addTlv(buffer, DHCP_VENDOR_INFO, mVendorInfoBytes);
- }
- if (mLeaseTimeBytes != null) {
- addTlv(buffer, DHCP_LEASE_TIME, mLeaseTimeBytes);
- }
- if (mNetmaskBytes != null) {
- addTlv(buffer, DHCP_SUBNET_MASK, mNetmaskBytes);
- }
- addTlvEnd(buffer);
- }
-
- // Convenience method.
- public ByteBuffer build() {
- // ENCAP_BOOTP packets don't contain ports, so just pass in 0.
- ByteBuffer pkt = buildPacket(ENCAP_BOOTP, (short) 0, (short) 0);
- pkt.flip();
- return pkt;
- }
- }
-
- private void assertDomainAndVendorInfoParses(
- String expectedDomain, byte[] domainBytes,
- String expectedVendorInfo, byte[] vendorInfoBytes) throws Exception {
- ByteBuffer packet = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER)
- .setDomainBytes(domainBytes)
- .setVendorInfoBytes(vendorInfoBytes)
- .build();
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
- assertEquals(expectedDomain, offerPacket.mDomainName);
- assertEquals(expectedVendorInfo, offerPacket.mVendorInfo);
- }
-
- @Test
- public void testDomainName() throws Exception {
- byte[] nullByte = new byte[] { 0x00 };
- byte[] twoNullBytes = new byte[] { 0x00, 0x00 };
- byte[] nonNullDomain = new byte[] {
- (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l'
- };
- byte[] trailingNullDomain = new byte[] {
- (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l', 0x00
- };
- byte[] embeddedNullsDomain = new byte[] {
- (byte) 'g', (byte) 'o', (byte) 'o', 0x00, 0x00, (byte) 'g', (byte) 'l'
- };
- byte[] metered = "ANDROID_METERED".getBytes("US-ASCII");
-
- byte[] meteredEmbeddedNull = metered.clone();
- meteredEmbeddedNull[7] = (char) 0;
-
- byte[] meteredTrailingNull = metered.clone();
- meteredTrailingNull[meteredTrailingNull.length - 1] = (char) 0;
-
- assertDomainAndVendorInfoParses("", nullByte, "\u0000", nullByte);
- assertDomainAndVendorInfoParses("", twoNullBytes, "\u0000\u0000", twoNullBytes);
- assertDomainAndVendorInfoParses("goo.gl", nonNullDomain, "ANDROID_METERED", metered);
- assertDomainAndVendorInfoParses("goo", embeddedNullsDomain,
- "ANDROID\u0000METERED", meteredEmbeddedNull);
- assertDomainAndVendorInfoParses("goo.gl", trailingNullDomain,
- "ANDROID_METERE\u0000", meteredTrailingNull);
- }
-
- private void assertLeaseTimeParses(boolean expectValid, Integer rawLeaseTime,
- long leaseTimeMillis, byte[] leaseTimeBytes) throws Exception {
- TestDhcpPacket testPacket = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER);
- if (leaseTimeBytes != null) {
- testPacket.setLeaseTimeBytes(leaseTimeBytes);
- }
- ByteBuffer packet = testPacket.build();
- DhcpPacket offerPacket = null;
-
- if (!expectValid) {
- try {
- offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
- fail("Invalid packet parsed successfully: " + offerPacket);
- } catch (ParseException expected) {
- }
- return;
- }
-
- offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
- assertNotNull(offerPacket);
- assertEquals(rawLeaseTime, offerPacket.mLeaseTime);
- DhcpResults dhcpResults = offerPacket.toDhcpResults(); // Just check this doesn't crash.
- assertEquals(leaseTimeMillis, offerPacket.getLeaseTimeMillis());
- }
-
- @Test
- public void testLeaseTime() throws Exception {
- byte[] noLease = null;
- byte[] tooShortLease = new byte[] { 0x00, 0x00 };
- byte[] tooLongLease = new byte[] { 0x00, 0x00, 0x00, 60, 0x01 };
- byte[] zeroLease = new byte[] { 0x00, 0x00, 0x00, 0x00 };
- byte[] tenSecondLease = new byte[] { 0x00, 0x00, 0x00, 10 };
- byte[] oneMinuteLease = new byte[] { 0x00, 0x00, 0x00, 60 };
- byte[] fiveMinuteLease = new byte[] { 0x00, 0x00, 0x01, 0x2c };
- byte[] oneDayLease = new byte[] { 0x00, 0x01, 0x51, (byte) 0x80 };
- byte[] maxIntPlusOneLease = new byte[] { (byte) 0x80, 0x00, 0x00, 0x01 };
- byte[] infiniteLease = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
-
- assertLeaseTimeParses(true, null, 0, noLease);
- assertLeaseTimeParses(false, null, 0, tooShortLease);
- assertLeaseTimeParses(false, null, 0, tooLongLease);
- assertLeaseTimeParses(true, 0, 60 * 1000, zeroLease);
- assertLeaseTimeParses(true, 10, 60 * 1000, tenSecondLease);
- assertLeaseTimeParses(true, 60, 60 * 1000, oneMinuteLease);
- assertLeaseTimeParses(true, 300, 300 * 1000, fiveMinuteLease);
- assertLeaseTimeParses(true, 86400, 86400 * 1000, oneDayLease);
- assertLeaseTimeParses(true, -2147483647, 2147483649L * 1000, maxIntPlusOneLease);
- assertLeaseTimeParses(true, DhcpPacket.INFINITE_LEASE, 0, infiniteLease);
- }
-
- private void checkIpAddress(String expected, Inet4Address clientIp, Inet4Address yourIp,
- byte[] netmaskBytes) throws Exception {
- checkIpAddress(expected, DHCP_MESSAGE_TYPE_OFFER, clientIp, yourIp, netmaskBytes);
- checkIpAddress(expected, DHCP_MESSAGE_TYPE_ACK, clientIp, yourIp, netmaskBytes);
- }
-
- private void checkIpAddress(String expected, byte type,
- Inet4Address clientIp, Inet4Address yourIp,
- byte[] netmaskBytes) throws Exception {
- ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp)
- .setNetmaskBytes(netmaskBytes)
- .build();
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
- DhcpResults results = offerPacket.toDhcpResults();
-
- if (expected != null) {
- LinkAddress expectedAddress = new LinkAddress(expected);
- assertEquals(expectedAddress, results.ipAddress);
- } else {
- assertNull(results);
- }
- }
-
- @Test
- public void testIpAddress() throws Exception {
- byte[] slash11Netmask = new byte[] { (byte) 0xff, (byte) 0xe0, 0x00, 0x00 };
- byte[] slash24Netmask = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x00 };
- byte[] invalidNetmask = new byte[] { (byte) 0xff, (byte) 0xfb, (byte) 0xff, 0x00 };
- Inet4Address example1 = v4Address("192.0.2.1");
- Inet4Address example2 = v4Address("192.0.2.43");
-
- // A packet without any addresses is not valid.
- checkIpAddress(null, ANY, ANY, slash24Netmask);
-
- // ClientIP is used iff YourIP is not present.
- checkIpAddress("192.0.2.1/24", example2, example1, slash24Netmask);
- checkIpAddress("192.0.2.43/11", example2, ANY, slash11Netmask);
- checkIpAddress("192.0.2.43/11", ANY, example2, slash11Netmask);
-
- // Invalid netmasks are ignored.
- checkIpAddress(null, example2, ANY, invalidNetmask);
-
- // If there is no netmask, implicit netmasks are used.
- checkIpAddress("192.0.2.43/24", ANY, example2, null);
- }
-
- private void assertDhcpResults(String ipAddress, String gateway, String dnsServersString,
- String domains, String serverAddress, String serverHostName, String vendorInfo,
- int leaseDuration, boolean hasMeteredHint, int mtu, DhcpResults dhcpResults)
- throws Exception {
- assertEquals(new LinkAddress(ipAddress), dhcpResults.ipAddress);
- assertEquals(v4Address(gateway), dhcpResults.gateway);
-
- String[] dnsServerStrings = dnsServersString.split(",");
- ArrayList dnsServers = new ArrayList();
- for (String dnsServerString : dnsServerStrings) {
- dnsServers.add(v4Address(dnsServerString));
- }
- assertEquals(dnsServers, dhcpResults.dnsServers);
-
- assertEquals(domains, dhcpResults.domains);
- assertEquals(v4Address(serverAddress), dhcpResults.serverAddress);
- assertEquals(serverHostName, dhcpResults.serverHostName);
- assertEquals(vendorInfo, dhcpResults.vendorInfo);
- assertEquals(leaseDuration, dhcpResults.leaseDuration);
- assertEquals(hasMeteredHint, dhcpResults.hasMeteredHint());
- assertEquals(mtu, dhcpResults.mtu);
- }
-
- @Test
- public void testOffer1() throws Exception {
- // TODO: Turn all of these into golden files. This will probably require using
- // androidx.test.InstrumentationRegistry for obtaining a Context object
- // to read such golden files, along with an appropriate Android.mk.
- // CHECKSTYLE:OFF Generated code
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // IP header.
- "451001480000000080118849c0a89003c0a89ff7" +
- // UDP header.
- "004300440134dcfa" +
- // BOOTP header.
- "02010600c997a63b0000000000000000c0a89ff70000000000000000" +
- // MAC address.
- "30766ff2a90c00000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options
- "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" +
- "3a0400000e103b040000189cff00000000000000000000"));
- // CHECKSTYLE:ON Generated code
-
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
- assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
- DhcpResults dhcpResults = offerPacket.toDhcpResults();
- assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4",
- null, "192.168.144.3", "", null, 7200, false, 0, dhcpResults);
- }
-
- @Test
- public void testOffer2() throws Exception {
- // CHECKSTYLE:OFF Generated code
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // IP header.
- "450001518d0600004011144dc0a82b01c0a82bf7" +
- // UDP header.
- "00430044013d9ac7" +
- // BOOTP header.
- "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
- // MAC address.
- "30766ff2a90c00000000000000000000" +
- // Server name ("dhcp.android.com" plus invalid "AAAA" after null terminator).
- "646863702e616e64726f69642e636f6d00000000000000000000000000000000" +
- "0000000000004141414100000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options
- "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
- "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"));
- // CHECKSTYLE:ON Generated code
-
- assertEquals(337, packet.limit());
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
- assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
- DhcpResults dhcpResults = offerPacket.toDhcpResults();
- assertDhcpResults("192.168.43.247/24", "192.168.43.1", "192.168.43.1",
- null, "192.168.43.1", "dhcp.android.com", "ANDROID_METERED", 3600, true, 0,
- dhcpResults);
- assertTrue(dhcpResults.hasMeteredHint());
- }
-
- @Test
- public void testBadIpPacket() throws Exception {
- final byte[] packet = HexDump.hexStringToByteArray(
- // IP header.
- "450001518d0600004011144dc0a82b01c0a82bf7");
-
- try {
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
- } catch (DhcpPacket.ParseException expected) {
- assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode);
- return;
- }
- fail("Dhcp packet parsing should have failed");
- }
-
- @Test
- public void testBadDhcpPacket() throws Exception {
- final byte[] packet = HexDump.hexStringToByteArray(
- // IP header.
- "450001518d0600004011144dc0a82b01c0a82bf7" +
- // UDP header.
- "00430044013d9ac7" +
- // BOOTP header.
- "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000");
-
- try {
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
- } catch (DhcpPacket.ParseException expected) {
- assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode);
- return;
- }
- fail("Dhcp packet parsing should have failed");
- }
-
- @Test
- public void testBadTruncatedOffer() throws Exception {
- final byte[] packet = HexDump.hexStringToByteArray(
- // IP header.
- "450001518d0600004011144dc0a82b01c0a82bf7" +
- // UDP header.
- "00430044013d9ac7" +
- // BOOTP header.
- "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
- // MAC address.
- "30766ff2a90c00000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File, missing one byte
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "00000000000000000000000000000000000000000000000000000000000000");
-
- try {
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
- } catch (DhcpPacket.ParseException expected) {
- assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode);
- return;
- }
- fail("Dhcp packet parsing should have failed");
- }
-
- @Test
- public void testBadOfferWithoutACookie() throws Exception {
- final byte[] packet = HexDump.hexStringToByteArray(
- // IP header.
- "450001518d0600004011144dc0a82b01c0a82bf7" +
- // UDP header.
- "00430044013d9ac7" +
- // BOOTP header.
- "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
- // MAC address.
- "30766ff2a90c00000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000"
- // No options
- );
-
- try {
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
- } catch (DhcpPacket.ParseException expected) {
- assertDhcpErrorCodes(DhcpErrorEvent.DHCP_NO_COOKIE, expected.errorCode);
- return;
- }
- fail("Dhcp packet parsing should have failed");
- }
-
- @Test
- public void testOfferWithBadCookie() throws Exception {
- final byte[] packet = HexDump.hexStringToByteArray(
- // IP header.
- "450001518d0600004011144dc0a82b01c0a82bf7" +
- // UDP header.
- "00430044013d9ac7" +
- // BOOTP header.
- "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
- // MAC address.
- "30766ff2a90c00000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Bad cookie
- "DEADBEEF3501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
- "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff");
-
- try {
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
- } catch (DhcpPacket.ParseException expected) {
- assertDhcpErrorCodes(DhcpErrorEvent.DHCP_BAD_MAGIC_COOKIE, expected.errorCode);
- return;
- }
- fail("Dhcp packet parsing should have failed");
- }
-
- private void assertDhcpErrorCodes(int expected, int got) {
- assertEquals(Integer.toHexString(expected), Integer.toHexString(got));
- }
-
- @Test
- public void testTruncatedOfferPackets() throws Exception {
- final byte[] packet = HexDump.hexStringToByteArray(
- // IP header.
- "450001518d0600004011144dc0a82b01c0a82bf7" +
- // UDP header.
- "00430044013d9ac7" +
- // BOOTP header.
- "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
- // MAC address.
- "30766ff2a90c00000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options
- "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
- "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff");
-
- for (int len = 0; len < packet.length; len++) {
- try {
- DhcpPacket.decodeFullPacket(packet, len, ENCAP_L3);
- } catch (ParseException e) {
- if (e.errorCode == DhcpErrorEvent.PARSING_ERROR) {
- fail(String.format("bad truncated packet of length %d", len));
- }
- }
- }
- }
-
- @Test
- public void testRandomPackets() throws Exception {
- final int maxRandomPacketSize = 512;
- final Random r = new Random();
- for (int i = 0; i < 10000; i++) {
- byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
- r.nextBytes(packet);
- try {
- DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
- } catch (ParseException e) {
- if (e.errorCode == DhcpErrorEvent.PARSING_ERROR) {
- fail("bad packet: " + HexDump.toHexString(packet));
- }
- }
- }
- }
-
- private byte[] mtuBytes(int mtu) {
- // 0x1a02: option 26, length 2. 0xff: no more options.
- if (mtu > Short.MAX_VALUE - Short.MIN_VALUE) {
- throw new IllegalArgumentException(
- String.format("Invalid MTU %d, must be 16-bit unsigned", mtu));
- }
- String hexString = String.format("1a02%04xff", mtu);
- return HexDump.hexStringToByteArray(hexString);
- }
-
- private void checkMtu(ByteBuffer packet, int expectedMtu, byte[] mtuBytes) throws Exception {
- if (mtuBytes != null) {
- packet.position(packet.capacity() - mtuBytes.length);
- packet.put(mtuBytes);
- packet.clear();
- }
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
- assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
- DhcpResults dhcpResults = offerPacket.toDhcpResults();
- assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4",
- null, "192.168.144.3", "", null, 7200, false, expectedMtu, dhcpResults);
- }
-
- @Test
- public void testMtu() throws Exception {
- // CHECKSTYLE:OFF Generated code
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // IP header.
- "451001480000000080118849c0a89003c0a89ff7" +
- // UDP header.
- "004300440134dcfa" +
- // BOOTP header.
- "02010600c997a63b0000000000000000c0a89ff70000000000000000" +
- // MAC address.
- "30766ff2a90c00000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options
- "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" +
- "3a0400000e103b040000189cff00000000"));
- // CHECKSTYLE:ON Generated code
-
- checkMtu(packet, 0, null);
- checkMtu(packet, 0, mtuBytes(1501));
- checkMtu(packet, 1500, mtuBytes(1500));
- checkMtu(packet, 1499, mtuBytes(1499));
- checkMtu(packet, 1280, mtuBytes(1280));
- checkMtu(packet, 0, mtuBytes(1279));
- checkMtu(packet, 0, mtuBytes(576));
- checkMtu(packet, 0, mtuBytes(68));
- checkMtu(packet, 0, mtuBytes(Short.MIN_VALUE));
- checkMtu(packet, 0, mtuBytes(Short.MAX_VALUE + 3));
- checkMtu(packet, 0, mtuBytes(-1));
- }
-
- @Test
- public void testBadHwaddrLength() throws Exception {
- // CHECKSTYLE:OFF Generated code
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // IP header.
- "450001518d0600004011144dc0a82b01c0a82bf7" +
- // UDP header.
- "00430044013d9ac7" +
- // BOOTP header.
- "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
- // MAC address.
- "30766ff2a90c00000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options
- "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
- "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"));
- // CHECKSTYLE:ON Generated code
- String expectedClientMac = "30766FF2A90C";
-
- final int hwAddrLenOffset = 20 + 8 + 2;
- assertEquals(6, packet.get(hwAddrLenOffset));
-
- // Expect the expected.
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
- assertNotNull(offerPacket);
- assertEquals(6, offerPacket.getClientMac().length);
- assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
-
- // Reduce the hardware address length and verify that it shortens the client MAC.
- packet.flip();
- packet.put(hwAddrLenOffset, (byte) 5);
- offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
- assertNotNull(offerPacket);
- assertEquals(5, offerPacket.getClientMac().length);
- assertEquals(expectedClientMac.substring(0, 10),
- HexDump.toHexString(offerPacket.getClientMac()));
-
- packet.flip();
- packet.put(hwAddrLenOffset, (byte) 3);
- offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
- assertNotNull(offerPacket);
- assertEquals(3, offerPacket.getClientMac().length);
- assertEquals(expectedClientMac.substring(0, 6),
- HexDump.toHexString(offerPacket.getClientMac()));
-
- // Set the the hardware address length to 0xff and verify that we a) don't treat it as -1
- // and crash, and b) hardcode it to 6.
- packet.flip();
- packet.put(hwAddrLenOffset, (byte) -1);
- offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
- assertNotNull(offerPacket);
- assertEquals(6, offerPacket.getClientMac().length);
- assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
-
- // Set the the hardware address length to a positive invalid value (> 16) and verify that we
- // hardcode it to 6.
- packet.flip();
- packet.put(hwAddrLenOffset, (byte) 17);
- offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
- assertNotNull(offerPacket);
- assertEquals(6, offerPacket.getClientMac().length);
- assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
- }
-
- @Test
- public void testPadAndOverloadedOptionsOffer() throws Exception {
- // A packet observed in the real world that is interesting for two reasons:
- //
- // 1. It uses pad bytes, which we previously didn't support correctly.
- // 2. It uses DHCP option overloading, which we don't currently support (but it doesn't
- // store any information in the overloaded fields).
- //
- // For now, we just check that it parses correctly.
- // CHECKSTYLE:OFF Generated code
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // Ethernet header.
- "b4cef6000000e80462236e300800" +
- // IP header.
- "4500014c00000000ff11741701010101ac119876" +
- // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
- "004300440138ae5a" +
- // BOOTP header.
- "020106000fa0059f0000000000000000ac1198760000000000000000" +
- // MAC address.
- "b4cef600000000000000000000000000" +
- // Server name.
- "ff00000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "ff00000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options
- "638253633501023604010101010104ffff000033040000a8c03401030304ac1101010604ac110101" +
- "0000000000000000000000000000000000000000000000ff000000"));
- // CHECKSTYLE:ON Generated code
-
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
- assertTrue(offerPacket instanceof DhcpOfferPacket);
- DhcpResults dhcpResults = offerPacket.toDhcpResults();
- assertDhcpResults("172.17.152.118/16", "172.17.1.1", "172.17.1.1",
- null, "1.1.1.1", "", null, 43200, false, 0, dhcpResults);
- }
-
- @Test
- public void testBug2111() throws Exception {
- // CHECKSTYLE:OFF Generated code
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // IP header.
- "4500014c00000000ff119beac3eaf3880a3f5d04" +
- // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
- "0043004401387464" +
- // BOOTP header.
- "0201060002554812000a0000000000000a3f5d040000000000000000" +
- // MAC address.
- "00904c00000000000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options.
- "638253633501023604c00002fe33040000bfc60104fffff00003040a3f50010608c0000201c0000202" +
- "0f0f646f6d61696e3132332e636f2e756b0000000000ff00000000"));
- // CHECKSTYLE:ON Generated code
-
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
- assertTrue(offerPacket instanceof DhcpOfferPacket);
- DhcpResults dhcpResults = offerPacket.toDhcpResults();
- assertDhcpResults("10.63.93.4/20", "10.63.80.1", "192.0.2.1,192.0.2.2",
- "domain123.co.uk", "192.0.2.254", "", null, 49094, false, 0, dhcpResults);
- }
-
- @Test
- public void testBug2136() throws Exception {
- // CHECKSTYLE:OFF Generated code
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // Ethernet header.
- "bcf5ac000000d0c7890000000800" +
- // IP header.
- "4500014c00000000ff119beac3eaf3880a3f5d04" +
- // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
- "0043004401387574" +
- // BOOTP header.
- "0201060163339a3000050000000000000a209ecd0000000000000000" +
- // MAC address.
- "bcf5ac00000000000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options.
- "6382536335010236040a20ff80330400001c200104fffff00003040a20900106089458413494584135" +
- "0f0b6c616e63732e61632e756b000000000000000000ff00000000"));
- // CHECKSTYLE:ON Generated code
-
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
- assertTrue(offerPacket instanceof DhcpOfferPacket);
- assertEquals("BCF5AC000000", HexDump.toHexString(offerPacket.getClientMac()));
- DhcpResults dhcpResults = offerPacket.toDhcpResults();
- assertDhcpResults("10.32.158.205/20", "10.32.144.1", "148.88.65.52,148.88.65.53",
- "lancs.ac.uk", "10.32.255.128", "", null, 7200, false, 0, dhcpResults);
- }
-
- @Test
- public void testUdpServerAnySourcePort() throws Exception {
- // CHECKSTYLE:OFF Generated code
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // Ethernet header.
- "9cd917000000001c2e0000000800" +
- // IP header.
- "45a00148000040003d115087d18194fb0a0f7af2" +
- // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
- // NOTE: The server source port is not the canonical port 67.
- "C29F004401341268" +
- // BOOTP header.
- "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" +
- // MAC address.
- "9cd91700000000000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options.
- "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
- "d18180060f0777766d2e6564751c040a0fffffff000000"));
- // CHECKSTYLE:ON Generated code
-
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
- assertTrue(offerPacket instanceof DhcpOfferPacket);
- assertEquals("9CD917000000", HexDump.toHexString(offerPacket.getClientMac()));
- DhcpResults dhcpResults = offerPacket.toDhcpResults();
- assertDhcpResults("10.15.122.242/16", "10.15.200.23",
- "209.129.128.3,209.129.148.3,209.129.128.6",
- "wvm.edu", "10.1.105.252", "", null, 86400, false, 0, dhcpResults);
- }
-
- @Test
- public void testUdpInvalidDstPort() throws Exception {
- // CHECKSTYLE:OFF Generated code
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // Ethernet header.
- "9cd917000000001c2e0000000800" +
- // IP header.
- "45a00148000040003d115087d18194fb0a0f7af2" +
- // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
- // NOTE: The destination port is a non-DHCP port.
- "0043aaaa01341268" +
- // BOOTP header.
- "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" +
- // MAC address.
- "9cd91700000000000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options.
- "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
- "d18180060f0777766d2e6564751c040a0fffffff000000"));
- // CHECKSTYLE:ON Generated code
-
- try {
- DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
- fail("Packet with invalid dst port did not throw ParseException");
- } catch (ParseException expected) {}
- }
-
- @Test
- public void testMultipleRouters() throws Exception {
- // CHECKSTYLE:OFF Generated code
- final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
- // Ethernet header.
- "fc3d93000000" + "081735000000" + "0800" +
- // IP header.
- "45000148c2370000ff117ac2c0a8bd02ffffffff" +
- // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
- "0043004401343beb" +
- // BOOTP header.
- "0201060027f518e20000800000000000c0a8bd310000000000000000" +
- // MAC address.
- "fc3d9300000000000000000000000000" +
- // Server name.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // File.
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- // Options.
- "638253633501023604c0abbd023304000070803a04000038403b04000062700104ffffff00" +
- "0308c0a8bd01ffffff0006080808080808080404ff000000000000"));
- // CHECKSTYLE:ON Generated code
-
- DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
- assertTrue(offerPacket instanceof DhcpOfferPacket);
- assertEquals("FC3D93000000", HexDump.toHexString(offerPacket.getClientMac()));
- DhcpResults dhcpResults = offerPacket.toDhcpResults();
- assertDhcpResults("192.168.189.49/24", "192.168.189.1", "8.8.8.8,8.8.4.4",
- null, "192.171.189.2", "", null, 28800, false, 0, dhcpResults);
- }
-
- @Test
- public void testDiscoverPacket() throws Exception {
- short secs = 7;
- int transactionId = 0xdeadbeef;
- byte[] hwaddr = {
- (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a
- };
-
- ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
- DhcpPacket.ENCAP_L2, transactionId, secs, hwaddr,
- false /* do unicast */, DhcpClient.REQUESTED_PARAMS);
-
- byte[] headers = new byte[] {
- // Ethernet header.
- (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
- (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
- (byte) 0x08, (byte) 0x00,
- // IP header.
- (byte) 0x45, (byte) 0x10, (byte) 0x01, (byte) 0x56,
- (byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x00,
- (byte) 0x40, (byte) 0x11, (byte) 0x39, (byte) 0x88,
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
- // UDP header.
- (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x43,
- (byte) 0x01, (byte) 0x42, (byte) 0x6a, (byte) 0x4a,
- // BOOTP.
- (byte) 0x01, (byte) 0x01, (byte) 0x06, (byte) 0x00,
- (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
- (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00,
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b,
- (byte) 0xb1, (byte) 0x7a
- };
- byte[] options = new byte[] {
- // Magic cookie 0x63825363.
- (byte) 0x63, (byte) 0x82, (byte) 0x53, (byte) 0x63,
- // Message type DISCOVER.
- (byte) 0x35, (byte) 0x01, (byte) 0x01,
- // Client identifier Ethernet, da:01:19:5b:b1:7a.
- (byte) 0x3d, (byte) 0x07,
- (byte) 0x01,
- (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
- // Max message size 1500.
- (byte) 0x39, (byte) 0x02, (byte) 0x05, (byte) 0xdc,
- // Version "android-dhcp-???".
- (byte) 0x3c, (byte) 0x10,
- 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-', 'd', 'h', 'c', 'p', '-', '?', '?', '?',
- // Hostname "android-01234567890abcde"
- (byte) 0x0c, (byte) 0x18,
- 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-',
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e',
- // Requested parameter list.
- (byte) 0x37, (byte) 0x0a,
- DHCP_SUBNET_MASK,
- DHCP_ROUTER,
- DHCP_DNS_SERVER,
- DHCP_DOMAIN_NAME,
- DHCP_MTU,
- DHCP_BROADCAST_ADDRESS,
- DHCP_LEASE_TIME,
- DHCP_RENEWAL_TIME,
- DHCP_REBINDING_TIME,
- DHCP_VENDOR_INFO,
- // End options.
- (byte) 0xff,
- // Our packets are always of even length. TODO: find out why and possibly fix it.
- (byte) 0x00
- };
- byte[] expected = new byte[DhcpPacket.MIN_PACKET_LENGTH_L2 + options.length];
- assertTrue((expected.length & 1) == 0);
- System.arraycopy(headers, 0, expected, 0, headers.length);
- System.arraycopy(options, 0, expected, DhcpPacket.MIN_PACKET_LENGTH_L2, options.length);
-
- byte[] actual = new byte[packet.limit()];
- packet.get(actual);
- String msg =
- "Expected:\n " + Arrays.toString(expected) +
- "\nActual:\n " + Arrays.toString(actual);
- assertTrue(msg, Arrays.equals(expected, actual));
- }
-
- public void checkBuildOfferPacket(int leaseTimeSecs, @Nullable String hostname)
- throws Exception {
- final int renewalTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) / 2);
- final int rebindingTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) * 875 / 1000);
- final int transactionId = 0xdeadbeef;
-
- final ByteBuffer packet = DhcpPacket.buildOfferPacket(
- DhcpPacket.ENCAP_BOOTP, transactionId, false /* broadcast */,
- SERVER_ADDR, INADDR_ANY /* relayIp */, CLIENT_ADDR /* yourIp */,
- CLIENT_MAC, leaseTimeSecs, NETMASK /* netMask */,
- BROADCAST_ADDR /* bcAddr */, Collections.singletonList(SERVER_ADDR) /* gateways */,
- Collections.singletonList(SERVER_ADDR) /* dnsServers */,
- SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, hostname,
- false /* metered */, MTU);
-
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- // BOOTP headers
- bos.write(new byte[] {
- (byte) 0x02, (byte) 0x01, (byte) 0x06, (byte) 0x00,
- (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- // ciaddr
- (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
- });
- // yiaddr
- bos.write(CLIENT_ADDR.getAddress());
- // siaddr
- bos.write(SERVER_ADDR.getAddress());
- // giaddr
- bos.write(INADDR_ANY.getAddress());
- // chaddr
- bos.write(CLIENT_MAC);
-
- // Padding
- bos.write(new byte[202]);
-
- // Options
- bos.write(new byte[]{
- // Magic cookie 0x63825363.
- (byte) 0x63, (byte) 0x82, (byte) 0x53, (byte) 0x63,
- // Message type OFFER.
- (byte) 0x35, (byte) 0x01, (byte) 0x02,
- });
- // Server ID
- bos.write(new byte[] { (byte) 0x36, (byte) 0x04 });
- bos.write(SERVER_ADDR.getAddress());
- // Lease time
- bos.write(new byte[] { (byte) 0x33, (byte) 0x04 });
- bos.write(intToByteArray(leaseTimeSecs));
- if (leaseTimeSecs != INFINITE_LEASE) {
- // Renewal time
- bos.write(new byte[]{(byte) 0x3a, (byte) 0x04});
- bos.write(intToByteArray(renewalTime));
- // Rebinding time
- bos.write(new byte[]{(byte) 0x3b, (byte) 0x04});
- bos.write(intToByteArray(rebindingTime));
- }
- // Subnet mask
- bos.write(new byte[] { (byte) 0x01, (byte) 0x04 });
- bos.write(NETMASK.getAddress());
- // Broadcast address
- bos.write(new byte[] { (byte) 0x1c, (byte) 0x04 });
- bos.write(BROADCAST_ADDR.getAddress());
- // Router
- bos.write(new byte[] { (byte) 0x03, (byte) 0x04 });
- bos.write(SERVER_ADDR.getAddress());
- // Nameserver
- bos.write(new byte[] { (byte) 0x06, (byte) 0x04 });
- bos.write(SERVER_ADDR.getAddress());
- // Hostname
- if (hostname != null) {
- bos.write(new byte[]{(byte) 0x0c, (byte) hostname.length()});
- bos.write(hostname.getBytes(Charset.forName("US-ASCII")));
- }
- // MTU
- bos.write(new byte[] { (byte) 0x1a, (byte) 0x02 });
- bos.write(shortToByteArray(MTU));
- // End options.
- bos.write(0xff);
-
- if ((bos.size() & 1) != 0) {
- bos.write(0x00);
- }
-
- final byte[] expected = bos.toByteArray();
- final byte[] actual = new byte[packet.limit()];
- packet.get(actual);
- final String msg = "Expected:\n " + HexDump.dumpHexString(expected) +
- "\nActual:\n " + HexDump.dumpHexString(actual);
- assertTrue(msg, Arrays.equals(expected, actual));
- }
-
- @Test
- public void testOfferPacket() throws Exception {
- checkBuildOfferPacket(3600, HOSTNAME);
- checkBuildOfferPacket(Integer.MAX_VALUE, HOSTNAME);
- checkBuildOfferPacket(0x80000000, HOSTNAME);
- checkBuildOfferPacket(INFINITE_LEASE, HOSTNAME);
- checkBuildOfferPacket(3600, null);
- }
-
- private static byte[] intToByteArray(int val) {
- return ByteBuffer.allocate(4).putInt(val).array();
- }
-
- private static byte[] shortToByteArray(short val) {
- return ByteBuffer.allocate(2).putShort(val).array();
- }
-}
diff --git a/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpServerTest.java b/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpServerTest.java
deleted file mode 100644
index f0e2f1b..0000000
--- a/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpServerTest.java
+++ /dev/null
@@ -1,333 +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 android.net.dhcp;
-
-import static android.net.InetAddresses.parseNumericAddress;
-import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
-import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
-import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
-import static android.net.dhcp.DhcpPacket.INADDR_ANY;
-import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
-import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
-
-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.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.never;
-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.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.INetworkStackStatusCallback;
-import android.net.LinkAddress;
-import android.net.MacAddress;
-import android.net.dhcp.DhcpLeaseRepository.InvalidAddressException;
-import android.net.dhcp.DhcpLeaseRepository.OutOfAddressesException;
-import android.net.dhcp.DhcpServer.Clock;
-import android.net.dhcp.DhcpServer.Dependencies;
-import android.net.util.SharedLog;
-import android.os.HandlerThread;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.net.Inet4Address;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-@RunWithLooper
-public class DhcpServerTest {
- private static final String TEST_IFACE = "testiface";
-
- private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2");
- private static final LinkAddress TEST_SERVER_LINKADDR = new LinkAddress(TEST_SERVER_ADDR, 20);
- private static final Set<Inet4Address> TEST_DEFAULT_ROUTERS = new HashSet<>(
- Arrays.asList(parseAddr("192.168.0.123"), parseAddr("192.168.0.124")));
- private static final Set<Inet4Address> TEST_DNS_SERVERS = new HashSet<>(
- Arrays.asList(parseAddr("192.168.0.126"), parseAddr("192.168.0.127")));
- private static final Set<Inet4Address> TEST_EXCLUDED_ADDRS = new HashSet<>(
- Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201")));
- private static final long TEST_LEASE_TIME_SECS = 3600L;
- private static final int TEST_MTU = 1500;
- private static final String TEST_HOSTNAME = "testhostname";
-
- private static final int TEST_TRANSACTION_ID = 123;
- private static final byte[] TEST_CLIENT_MAC_BYTES = new byte [] { 1, 2, 3, 4, 5, 6 };
- private static final MacAddress TEST_CLIENT_MAC = MacAddress.fromBytes(TEST_CLIENT_MAC_BYTES);
- private static final Inet4Address TEST_CLIENT_ADDR = parseAddr("192.168.0.42");
-
- private static final long TEST_CLOCK_TIME = 1234L;
- private static final int TEST_LEASE_EXPTIME_SECS = 3600;
- private static final DhcpLease TEST_LEASE = new DhcpLease(null, TEST_CLIENT_MAC,
- TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME,
- null /* hostname */);
- private static final DhcpLease TEST_LEASE_WITH_HOSTNAME = new DhcpLease(null, TEST_CLIENT_MAC,
- TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME, TEST_HOSTNAME);
-
- @NonNull @Mock
- private Dependencies mDeps;
- @NonNull @Mock
- private DhcpLeaseRepository mRepository;
- @NonNull @Mock
- private Clock mClock;
- @NonNull @Mock
- private DhcpPacketListener mPacketListener;
-
- @NonNull @Captor
- private ArgumentCaptor<ByteBuffer> mSentPacketCaptor;
- @NonNull @Captor
- private ArgumentCaptor<Inet4Address> mResponseDstAddrCaptor;
-
- @NonNull
- private HandlerThread mHandlerThread;
- @NonNull
- private TestableLooper mLooper;
- @NonNull
- private DhcpServer mServer;
-
- @Nullable
- private String mPrevShareClassloaderProp;
-
- private final INetworkStackStatusCallback mAssertSuccessCallback =
- new INetworkStackStatusCallback.Stub() {
- @Override
- public void onStatusAvailable(int statusCode) {
- assertEquals(STATUS_SUCCESS, statusCode);
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
- };
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- when(mDeps.makeLeaseRepository(any(), any(), any())).thenReturn(mRepository);
- when(mDeps.makeClock()).thenReturn(mClock);
- when(mDeps.makePacketListener()).thenReturn(mPacketListener);
- doNothing().when(mDeps)
- .sendPacket(any(), mSentPacketCaptor.capture(), mResponseDstAddrCaptor.capture());
- when(mClock.elapsedRealtime()).thenReturn(TEST_CLOCK_TIME);
-
- final DhcpServingParams servingParams = new DhcpServingParams.Builder()
- .setDefaultRouters(TEST_DEFAULT_ROUTERS)
- .setDhcpLeaseTimeSecs(TEST_LEASE_TIME_SECS)
- .setDnsServers(TEST_DNS_SERVERS)
- .setServerAddr(TEST_SERVER_LINKADDR)
- .setLinkMtu(TEST_MTU)
- .setExcludedAddrs(TEST_EXCLUDED_ADDRS)
- .build();
-
- mLooper = TestableLooper.get(this);
- mHandlerThread = spy(new HandlerThread("TestDhcpServer"));
- when(mHandlerThread.getLooper()).thenReturn(mLooper.getLooper());
- mServer = new DhcpServer(mHandlerThread, TEST_IFACE, servingParams,
- new SharedLog(DhcpServerTest.class.getSimpleName()), mDeps);
-
- mServer.start(mAssertSuccessCallback);
- mLooper.processAllMessages();
- }
-
- @After
- public void tearDown() throws Exception {
- mServer.stop(mAssertSuccessCallback);
- mLooper.processMessages(1);
- verify(mPacketListener, times(1)).stop();
- verify(mHandlerThread, times(1)).quitSafely();
- }
-
- @Test
- public void testStart() throws Exception {
- verify(mPacketListener, times(1)).start();
- }
-
- @Test
- public void testDiscover() throws Exception {
- // TODO: refactor packet construction to eliminate unnecessary/confusing/duplicate fields
- when(mRepository.getOffer(isNull() /* clientId */, eq(TEST_CLIENT_MAC),
- eq(INADDR_ANY) /* relayAddr */, isNull() /* reqAddr */, isNull() /* hostname */))
- .thenReturn(TEST_LEASE);
-
- final DhcpDiscoverPacket discover = new DhcpDiscoverPacket(TEST_TRANSACTION_ID,
- (short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
- false /* broadcast */, INADDR_ANY /* srcIp */);
- mServer.processPacket(discover, DHCP_CLIENT);
-
- assertResponseSentTo(TEST_CLIENT_ADDR);
- final DhcpOfferPacket packet = assertOffer(getPacket());
- assertMatchesTestLease(packet);
- }
-
- @Test
- public void testDiscover_OutOfAddresses() throws Exception {
- when(mRepository.getOffer(isNull() /* clientId */, eq(TEST_CLIENT_MAC),
- eq(INADDR_ANY) /* relayAddr */, isNull() /* reqAddr */, isNull() /* hostname */))
- .thenThrow(new OutOfAddressesException("Test exception"));
-
- final DhcpDiscoverPacket discover = new DhcpDiscoverPacket(TEST_TRANSACTION_ID,
- (short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
- false /* broadcast */, INADDR_ANY /* srcIp */);
- mServer.processPacket(discover, DHCP_CLIENT);
-
- assertResponseSentTo(INADDR_BROADCAST);
- final DhcpNakPacket packet = assertNak(getPacket());
- assertMatchesClient(packet);
- }
-
- private DhcpRequestPacket makeRequestSelectingPacket() {
- final DhcpRequestPacket request = new DhcpRequestPacket(TEST_TRANSACTION_ID,
- (short) 0 /* secs */, INADDR_ANY /* clientIp */, INADDR_ANY /* relayIp */,
- TEST_CLIENT_MAC_BYTES, false /* broadcast */);
- request.mServerIdentifier = TEST_SERVER_ADDR;
- request.mRequestedIp = TEST_CLIENT_ADDR;
- return request;
- }
-
- @Test
- public void testRequest_Selecting_Ack() throws Exception {
- when(mRepository.requestLease(isNull() /* clientId */, eq(TEST_CLIENT_MAC),
- eq(INADDR_ANY) /* clientAddr */, eq(INADDR_ANY) /* relayAddr */,
- eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, eq(TEST_HOSTNAME)))
- .thenReturn(TEST_LEASE_WITH_HOSTNAME);
-
- final DhcpRequestPacket request = makeRequestSelectingPacket();
- request.mHostName = TEST_HOSTNAME;
- request.mRequestedParams = new byte[] { DHCP_HOST_NAME };
- mServer.processPacket(request, DHCP_CLIENT);
-
- assertResponseSentTo(TEST_CLIENT_ADDR);
- final DhcpAckPacket packet = assertAck(getPacket());
- assertMatchesTestLease(packet, TEST_HOSTNAME);
- }
-
- @Test
- public void testRequest_Selecting_Nak() throws Exception {
- when(mRepository.requestLease(isNull(), eq(TEST_CLIENT_MAC),
- eq(INADDR_ANY) /* clientAddr */, eq(INADDR_ANY) /* relayAddr */,
- eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, isNull() /* hostname */))
- .thenThrow(new InvalidAddressException("Test error"));
-
- final DhcpRequestPacket request = makeRequestSelectingPacket();
- mServer.processPacket(request, DHCP_CLIENT);
-
- assertResponseSentTo(INADDR_BROADCAST);
- final DhcpNakPacket packet = assertNak(getPacket());
- assertMatchesClient(packet);
- }
-
- @Test
- public void testRequest_Selecting_WrongClientPort() throws Exception {
- final DhcpRequestPacket request = makeRequestSelectingPacket();
- mServer.processPacket(request, 50000);
-
- verify(mRepository, never())
- .requestLease(any(), any(), any(), any(), any(), anyBoolean(), any());
- verify(mDeps, never()).sendPacket(any(), any(), any());
- }
-
- @Test
- public void testRelease() throws Exception {
- final DhcpReleasePacket release = new DhcpReleasePacket(TEST_TRANSACTION_ID,
- TEST_SERVER_ADDR, TEST_CLIENT_ADDR,
- INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES);
- mServer.processPacket(release, DHCP_CLIENT);
-
- verify(mRepository, times(1))
- .releaseLease(isNull(), eq(TEST_CLIENT_MAC), eq(TEST_CLIENT_ADDR));
- }
-
- /* TODO: add more tests once packet construction is refactored, including:
- * - usage of giaddr
- * - usage of broadcast bit
- * - other request states (init-reboot/renewing/rebinding)
- */
-
- private void assertMatchesTestLease(@NonNull DhcpPacket packet, @Nullable String hostname) {
- assertMatchesClient(packet);
- assertFalse(packet.hasExplicitClientId());
- assertEquals(TEST_SERVER_ADDR, packet.mServerIdentifier);
- assertEquals(TEST_CLIENT_ADDR, packet.mYourIp);
- assertNotNull(packet.mLeaseTime);
- assertEquals(TEST_LEASE_EXPTIME_SECS, (int) packet.mLeaseTime);
- assertEquals(hostname, packet.mHostName);
- }
-
- private void assertMatchesTestLease(@NonNull DhcpPacket packet) {
- assertMatchesTestLease(packet, null);
- }
-
- private void assertMatchesClient(@NonNull DhcpPacket packet) {
- assertEquals(TEST_TRANSACTION_ID, packet.mTransId);
- assertEquals(TEST_CLIENT_MAC, MacAddress.fromBytes(packet.mClientMac));
- }
-
- private void assertResponseSentTo(@NonNull Inet4Address addr) {
- assertEquals(addr, mResponseDstAddrCaptor.getValue());
- }
-
- private static DhcpNakPacket assertNak(@Nullable DhcpPacket packet) {
- assertTrue(packet instanceof DhcpNakPacket);
- return (DhcpNakPacket) packet;
- }
-
- private static DhcpAckPacket assertAck(@Nullable DhcpPacket packet) {
- assertTrue(packet instanceof DhcpAckPacket);
- return (DhcpAckPacket) packet;
- }
-
- private static DhcpOfferPacket assertOffer(@Nullable DhcpPacket packet) {
- assertTrue(packet instanceof DhcpOfferPacket);
- return (DhcpOfferPacket) packet;
- }
-
- private DhcpPacket getPacket() throws Exception {
- verify(mDeps, times(1)).sendPacket(any(), any(), any());
- return DhcpPacket.decodeFullPacket(mSentPacketCaptor.getValue(), ENCAP_BOOTP);
- }
-
- private static Inet4Address parseAddr(@Nullable String inet4Addr) {
- return (Inet4Address) parseNumericAddress(inet4Addr);
- }
-}
diff --git a/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpServingParamsTest.java b/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpServingParamsTest.java
deleted file mode 100644
index 57a87a4..0000000
--- a/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpServingParamsTest.java
+++ /dev/null
@@ -1,221 +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 android.net.dhcp;
-
-import static android.net.InetAddresses.parseNumericAddress;
-import static android.net.dhcp.DhcpServingParams.MTU_UNSET;
-import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.LinkAddress;
-import android.net.dhcp.DhcpServingParams.InvalidParameterException;
-import android.net.shared.Inet4AddressUtils;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.lang.reflect.Modifier;
-import java.net.Inet4Address;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class DhcpServingParamsTest {
- @NonNull
- private DhcpServingParams.Builder mBuilder;
-
- private static final Set<Inet4Address> TEST_DEFAULT_ROUTERS = new HashSet<>(
- Arrays.asList(parseAddr("192.168.0.123"), parseAddr("192.168.0.124")));
- private static final long TEST_LEASE_TIME_SECS = 3600L;
- private static final Set<Inet4Address> TEST_DNS_SERVERS = new HashSet<>(
- Arrays.asList(parseAddr("192.168.0.126"), parseAddr("192.168.0.127")));
- private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2");
- private static final LinkAddress TEST_LINKADDR = new LinkAddress(TEST_SERVER_ADDR, 20);
- private static final int TEST_MTU = 1500;
- private static final Set<Inet4Address> TEST_EXCLUDED_ADDRS = new HashSet<>(
- Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201")));
- private static final boolean TEST_METERED = true;
-
- @Before
- public void setUp() {
- mBuilder = new DhcpServingParams.Builder()
- .setDefaultRouters(TEST_DEFAULT_ROUTERS)
- .setDhcpLeaseTimeSecs(TEST_LEASE_TIME_SECS)
- .setDnsServers(TEST_DNS_SERVERS)
- .setServerAddr(TEST_LINKADDR)
- .setLinkMtu(TEST_MTU)
- .setExcludedAddrs(TEST_EXCLUDED_ADDRS)
- .setMetered(TEST_METERED);
- }
-
- @Test
- public void testBuild_Immutable() throws InvalidParameterException {
- final Set<Inet4Address> routers = new HashSet<>(TEST_DEFAULT_ROUTERS);
- final Set<Inet4Address> dnsServers = new HashSet<>(TEST_DNS_SERVERS);
- final Set<Inet4Address> excludedAddrs = new HashSet<>(TEST_EXCLUDED_ADDRS);
-
- final DhcpServingParams params = mBuilder
- .setDefaultRouters(routers)
- .setDnsServers(dnsServers)
- .setExcludedAddrs(excludedAddrs)
- .build();
-
- // Modifications to source objects should not affect builder or final parameters
- final Inet4Address addedAddr = parseAddr("192.168.0.223");
- routers.add(addedAddr);
- dnsServers.add(addedAddr);
- excludedAddrs.add(addedAddr);
-
- assertEquals(TEST_DEFAULT_ROUTERS, params.defaultRouters);
- assertEquals(TEST_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs);
- assertEquals(TEST_DNS_SERVERS, params.dnsServers);
- assertEquals(TEST_LINKADDR, params.serverAddr);
- assertEquals(TEST_MTU, params.linkMtu);
- assertEquals(TEST_METERED, params.metered);
-
- assertContains(params.excludedAddrs, TEST_EXCLUDED_ADDRS);
- assertContains(params.excludedAddrs, TEST_DEFAULT_ROUTERS);
- assertContains(params.excludedAddrs, TEST_DNS_SERVERS);
- assertContains(params.excludedAddrs, TEST_SERVER_ADDR);
-
- assertFalse("excludedAddrs should not contain " + addedAddr,
- params.excludedAddrs.contains(addedAddr));
- }
-
- @Test(expected = InvalidParameterException.class)
- public void testBuild_NegativeLeaseTime() throws InvalidParameterException {
- mBuilder.setDhcpLeaseTimeSecs(-1).build();
- }
-
- @Test(expected = InvalidParameterException.class)
- public void testBuild_LeaseTimeTooLarge() throws InvalidParameterException {
- // Set lease time larger than max value for uint32
- mBuilder.setDhcpLeaseTimeSecs(1L << 32).build();
- }
-
- @Test
- public void testBuild_InfiniteLeaseTime() throws InvalidParameterException {
- final long infiniteLeaseTime = 0xffffffffL;
- final DhcpServingParams params = mBuilder
- .setDhcpLeaseTimeSecs(infiniteLeaseTime).build();
- assertEquals(infiniteLeaseTime, params.dhcpLeaseTimeSecs);
- assertTrue(params.dhcpLeaseTimeSecs > 0L);
- }
-
- @Test
- public void testBuild_UnsetMtu() throws InvalidParameterException {
- final DhcpServingParams params = mBuilder.setLinkMtu(MTU_UNSET).build();
- assertEquals(MTU_UNSET, params.linkMtu);
- }
-
- @Test(expected = InvalidParameterException.class)
- public void testBuild_MtuTooSmall() throws InvalidParameterException {
- mBuilder.setLinkMtu(20).build();
- }
-
- @Test(expected = InvalidParameterException.class)
- public void testBuild_MtuTooLarge() throws InvalidParameterException {
- mBuilder.setLinkMtu(65_536).build();
- }
-
- @Test(expected = InvalidParameterException.class)
- public void testBuild_IPv6Addr() throws InvalidParameterException {
- mBuilder.setServerAddr(new LinkAddress(parseNumericAddress("fe80::1111"), 120)).build();
- }
-
- @Test(expected = InvalidParameterException.class)
- public void testBuild_PrefixTooLarge() throws InvalidParameterException {
- mBuilder.setServerAddr(new LinkAddress(TEST_SERVER_ADDR, 15)).build();
- }
-
- @Test(expected = InvalidParameterException.class)
- public void testBuild_PrefixTooSmall() throws InvalidParameterException {
- mBuilder.setDefaultRouters(parseAddr("192.168.0.254"))
- .setServerAddr(new LinkAddress(TEST_SERVER_ADDR, 31))
- .build();
- }
-
- @Test(expected = InvalidParameterException.class)
- public void testBuild_RouterNotInPrefix() throws InvalidParameterException {
- mBuilder.setDefaultRouters(parseAddr("192.168.254.254")).build();
- }
-
- @Test
- public void testFromParcelableObject() throws InvalidParameterException {
- final DhcpServingParams params = mBuilder.build();
- final DhcpServingParamsParcel parcel = new DhcpServingParamsParcel();
- parcel.defaultRouters = toIntArray(TEST_DEFAULT_ROUTERS);
- parcel.dhcpLeaseTimeSecs = TEST_LEASE_TIME_SECS;
- parcel.dnsServers = toIntArray(TEST_DNS_SERVERS);
- parcel.serverAddr = inet4AddressToIntHTH(TEST_SERVER_ADDR);
- parcel.serverAddrPrefixLength = TEST_LINKADDR.getPrefixLength();
- parcel.linkMtu = TEST_MTU;
- parcel.excludedAddrs = toIntArray(TEST_EXCLUDED_ADDRS);
- parcel.metered = TEST_METERED;
- final DhcpServingParams parceled = DhcpServingParams.fromParcelableObject(parcel);
-
- assertEquals(params.defaultRouters, parceled.defaultRouters);
- assertEquals(params.dhcpLeaseTimeSecs, parceled.dhcpLeaseTimeSecs);
- assertEquals(params.dnsServers, parceled.dnsServers);
- assertEquals(params.serverAddr, parceled.serverAddr);
- assertEquals(params.linkMtu, parceled.linkMtu);
- assertEquals(params.excludedAddrs, parceled.excludedAddrs);
- assertEquals(params.metered, parceled.metered);
-
- // Ensure that we do not miss any field if added in the future
- final long numFields = Arrays.stream(DhcpServingParams.class.getDeclaredFields())
- .filter(f -> !Modifier.isStatic(f.getModifiers()))
- .count();
- assertEquals(7, numFields);
- }
-
- @Test(expected = InvalidParameterException.class)
- public void testFromParcelableObject_NullArgument() throws InvalidParameterException {
- DhcpServingParams.fromParcelableObject(null);
- }
-
- private static int[] toIntArray(Collection<Inet4Address> addrs) {
- return addrs.stream().mapToInt(Inet4AddressUtils::inet4AddressToIntHTH).toArray();
- }
-
- private static <T> void assertContains(@NonNull Set<T> set, @NonNull Set<T> subset) {
- for (final T elem : subset) {
- assertContains(set, elem);
- }
- }
-
- private static <T> void assertContains(@NonNull Set<T> set, @Nullable T elem) {
- assertTrue("Set does not contain " + elem, set.contains(elem));
- }
-
- @NonNull
- private static Inet4Address parseAddr(@NonNull String inet4Addr) {
- return (Inet4Address) parseNumericAddress(inet4Addr);
- }
-}
diff --git a/packages/NetworkStack/tests/unit/src/android/net/ip/IpClientTest.java b/packages/NetworkStack/tests/unit/src/android/net/ip/IpClientTest.java
deleted file mode 100644
index 5f80006..0000000
--- a/packages/NetworkStack/tests/unit/src/android/net/ip/IpClientTest.java
+++ /dev/null
@@ -1,547 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.app.AlarmManager;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.res.Resources;
-import android.net.ConnectivityManager;
-import android.net.INetd;
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-import android.net.LinkProperties;
-import android.net.MacAddress;
-import android.net.NetworkStackIpMemoryStore;
-import android.net.RouteInfo;
-import android.net.ipmemorystore.NetworkAttributes;
-import android.net.shared.InitialConfiguration;
-import android.net.shared.ProvisioningConfiguration;
-import android.net.util.InterfaceParams;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.R;
-import com.android.server.NetworkObserver;
-import com.android.server.NetworkObserverRegistry;
-import com.android.server.NetworkStackService;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.net.InetAddress;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Tests for IpClient.
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class IpClientTest {
- private static final int DEFAULT_AVOIDBADWIFI_CONFIG_VALUE = 1;
-
- private static final String VALID = "VALID";
- private static final String INVALID = "INVALID";
- private static final String TEST_IFNAME = "test_wlan0";
- private static final int TEST_IFINDEX = 1001;
- // See RFC 7042#section-2.1.2 for EUI-48 documentation values.
- private static final MacAddress TEST_MAC = MacAddress.fromString("00:00:5E:00:53:01");
- private static final int TEST_TIMEOUT_MS = 400;
- private static final String TEST_L2KEY = "some l2key";
- private static final String TEST_GROUPHINT = "some grouphint";
-
- @Mock private Context mContext;
- @Mock private ConnectivityManager mCm;
- @Mock private NetworkObserverRegistry mObserverRegistry;
- @Mock private INetd mNetd;
- @Mock private Resources mResources;
- @Mock private IIpClientCallbacks mCb;
- @Mock private AlarmManager mAlarm;
- @Mock private IpClient.Dependencies mDependencies;
- @Mock private ContentResolver mContentResolver;
- @Mock private NetworkStackService.NetworkStackServiceManager mNetworkStackServiceManager;
- @Mock private NetworkStackIpMemoryStore mIpMemoryStore;
-
- private NetworkObserver mObserver;
- private InterfaceParams mIfParams;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm);
- when(mContext.getSystemService(eq(ConnectivityManager.class))).thenReturn(mCm);
- when(mContext.getResources()).thenReturn(mResources);
- when(mDependencies.getNetd(any())).thenReturn(mNetd);
- when(mResources.getInteger(R.integer.config_networkAvoidBadWifi))
- .thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE);
- when(mContext.getContentResolver()).thenReturn(mContentResolver);
-
- mIfParams = null;
- }
-
- private void setTestInterfaceParams(String ifname) {
- mIfParams = (ifname != null)
- ? new InterfaceParams(ifname, TEST_IFINDEX, TEST_MAC)
- : null;
- when(mDependencies.getInterfaceParams(anyString())).thenReturn(mIfParams);
- }
-
- private IpClient makeIpClient(String ifname) throws Exception {
- setTestInterfaceParams(ifname);
- final IpClient ipc = new IpClient(mContext, ifname, mCb, mObserverRegistry,
- mNetworkStackServiceManager, mDependencies);
- verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(ifname, false);
- verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(ifname);
- ArgumentCaptor<NetworkObserver> arg = ArgumentCaptor.forClass(NetworkObserver.class);
- verify(mObserverRegistry, times(1)).registerObserverForNonblockingCallback(arg.capture());
- mObserver = arg.getValue();
- reset(mObserverRegistry);
- reset(mNetd);
- // Verify IpClient doesn't call onLinkPropertiesChange() when it starts.
- verify(mCb, never()).onLinkPropertiesChange(any());
- reset(mCb);
- return ipc;
- }
-
- private static LinkProperties makeEmptyLinkProperties(String iface) {
- final LinkProperties empty = new LinkProperties();
- empty.setInterfaceName(iface);
- return empty;
- }
-
- private void verifyNetworkAttributesStored(final String l2Key,
- final NetworkAttributes attributes) {
- // TODO : when storing is implemented, turn this on
- // verify(mIpMemoryStore).storeNetworkAttributes(eq(l2Key), eq(attributes), any());
- }
-
- @Test
- public void testNullInterfaceNameMostDefinitelyThrows() throws Exception {
- setTestInterfaceParams(null);
- try {
- final IpClient ipc = new IpClient(mContext, null, mCb, mObserverRegistry,
- mNetworkStackServiceManager, mDependencies);
- ipc.shutdown();
- fail();
- } catch (NullPointerException npe) {
- // Phew; null interface names not allowed.
- }
- }
-
- @Test
- public void testNullCallbackMostDefinitelyThrows() throws Exception {
- final String ifname = "lo";
- setTestInterfaceParams(ifname);
- try {
- final IpClient ipc = new IpClient(mContext, ifname, null, mObserverRegistry,
- mNetworkStackServiceManager, mDependencies);
- ipc.shutdown();
- fail();
- } catch (NullPointerException npe) {
- // Phew; null callbacks not allowed.
- }
- }
-
- @Test
- public void testInvalidInterfaceDoesNotThrow() throws Exception {
- setTestInterfaceParams(TEST_IFNAME);
- final IpClient ipc = new IpClient(mContext, TEST_IFNAME, mCb, mObserverRegistry,
- mNetworkStackServiceManager, mDependencies);
- verifyNoMoreInteractions(mIpMemoryStore);
- ipc.shutdown();
- }
-
- @Test
- public void testInterfaceNotFoundFailsImmediately() throws Exception {
- setTestInterfaceParams(null);
- final IpClient ipc = new IpClient(mContext, TEST_IFNAME, mCb, mObserverRegistry,
- mNetworkStackServiceManager, mDependencies);
- ipc.startProvisioning(new ProvisioningConfiguration());
- verify(mCb, times(1)).onProvisioningFailure(any());
- verify(mIpMemoryStore, never()).storeNetworkAttributes(any(), any(), any());
- ipc.shutdown();
- }
-
- @Test
- public void testDefaultProvisioningConfiguration() throws Exception {
- final String iface = TEST_IFNAME;
- final IpClient ipc = makeIpClient(iface);
-
- ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
- .withoutIPv4()
- // TODO: mock IpReachabilityMonitor's dependencies (NetworkInterface, PowerManager)
- // and enable it in this test
- .withoutIpReachabilityMonitor()
- .build();
-
- ipc.startProvisioning(config);
- verify(mCb, times(1)).setNeighborDiscoveryOffload(true);
- verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).setFallbackMulticastFilter(false);
- verify(mCb, never()).onProvisioningFailure(any());
- verify(mIpMemoryStore, never()).storeNetworkAttributes(any(), any(), any());
-
- ipc.shutdown();
- verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(iface, false);
- verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(iface);
- verify(mCb, timeout(TEST_TIMEOUT_MS).times(1))
- .onLinkPropertiesChange(makeEmptyLinkProperties(iface));
- }
-
- @Test
- public void testProvisioningWithInitialConfiguration() throws Exception {
- final String iface = TEST_IFNAME;
- final IpClient ipc = makeIpClient(iface);
- final String l2Key = TEST_L2KEY;
- final String groupHint = TEST_GROUPHINT;
-
- String[] addresses = {
- "fe80::a4be:f92:e1f7:22d1/64",
- "fe80::f04a:8f6:6a32:d756/64",
- "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"
- };
- String[] prefixes = { "fe80::/64", "fd2c:4e57:8e3c::/64" };
-
- ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
- .withoutIPv4()
- .withoutIpReachabilityMonitor()
- .withInitialConfiguration(conf(links(addresses), prefixes(prefixes), ips()))
- .build();
-
- ipc.startProvisioning(config);
- verify(mCb, times(1)).setNeighborDiscoveryOffload(true);
- verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).setFallbackMulticastFilter(false);
- verify(mCb, never()).onProvisioningFailure(any());
- ipc.setL2KeyAndGroupHint(l2Key, groupHint);
-
- for (String addr : addresses) {
- String[] parts = addr.split("/");
- verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1))
- .interfaceAddAddress(iface, parts[0], Integer.parseInt(parts[1]));
- }
-
- final int lastAddr = addresses.length - 1;
-
- // Add N - 1 addresses
- for (int i = 0; i < lastAddr; i++) {
- mObserver.onInterfaceAddressUpdated(new LinkAddress(addresses[i]), iface);
- verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(any());
- reset(mCb);
- }
-
- // Add Nth address
- mObserver.onInterfaceAddressUpdated(new LinkAddress(addresses[lastAddr]), iface);
- LinkProperties want = linkproperties(links(addresses), routes(prefixes));
- want.setInterfaceName(iface);
- verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).onProvisioningSuccess(want);
- verifyNetworkAttributesStored(l2Key, new NetworkAttributes.Builder()
- .setGroupHint(groupHint)
- .build());
-
- ipc.shutdown();
- verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(iface, false);
- verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(iface);
- verify(mCb, timeout(TEST_TIMEOUT_MS).times(1))
- .onLinkPropertiesChange(makeEmptyLinkProperties(iface));
- verifyNoMoreInteractions(mIpMemoryStore);
- }
-
- @Test
- public void testIsProvisioned() throws Exception {
- InitialConfiguration empty = conf(links(), prefixes());
- IsProvisionedTestCase[] testcases = {
- // nothing
- notProvisionedCase(links(), routes(), dns(), null),
- notProvisionedCase(links(), routes(), dns(), empty),
-
- // IPv4
- provisionedCase(links("192.0.2.12/24"), routes(), dns(), empty),
-
- // IPv6
- notProvisionedCase(
- links("fe80::a4be:f92:e1f7:22d1/64", "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"),
- routes(), dns(), empty),
- notProvisionedCase(
- links("fe80::a4be:f92:e1f7:22d1/64", "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"),
- routes("fe80::/64", "fd2c:4e57:8e3c::/64"), dns("fd00:1234:5678::1000"), empty),
- provisionedCase(
- links("2001:db8:dead:beef:f00::a0/64", "fe80::1/64"),
- routes("::/0"),
- dns("2001:db8:dead:beef:f00::02"), empty),
-
- // Initial configuration
- provisionedCase(
- links("fe80::e1f7:22d1/64", "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"),
- routes("fe80::/64", "fd2c:4e57:8e3c::/64"),
- dns(),
- conf(links("fe80::e1f7:22d1/64", "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"),
- prefixes( "fe80::/64", "fd2c:4e57:8e3c::/64"), ips()))
- };
-
- for (IsProvisionedTestCase testcase : testcases) {
- if (IpClient.isProvisioned(testcase.lp, testcase.config) != testcase.isProvisioned) {
- fail(testcase.errorMessage());
- }
- }
- }
-
- static class IsProvisionedTestCase {
- boolean isProvisioned;
- LinkProperties lp;
- InitialConfiguration config;
-
- String errorMessage() {
- return String.format("expected %s with config %s to be %s, but was %s",
- lp, config, provisioned(isProvisioned), provisioned(!isProvisioned));
- }
-
- static String provisioned(boolean isProvisioned) {
- return isProvisioned ? "provisioned" : "not provisioned";
- }
- }
-
- static IsProvisionedTestCase provisionedCase(Set<LinkAddress> lpAddrs, Set<RouteInfo> lpRoutes,
- Set<InetAddress> lpDns, InitialConfiguration config) {
- return provisioningTest(true, lpAddrs, lpRoutes, lpDns, config);
- }
-
- static IsProvisionedTestCase notProvisionedCase(Set<LinkAddress> lpAddrs,
- Set<RouteInfo> lpRoutes, Set<InetAddress> lpDns, InitialConfiguration config) {
- return provisioningTest(false, lpAddrs, lpRoutes, lpDns, config);
- }
-
- static IsProvisionedTestCase provisioningTest(boolean isProvisioned, Set<LinkAddress> lpAddrs,
- Set<RouteInfo> lpRoutes, Set<InetAddress> lpDns, InitialConfiguration config) {
- IsProvisionedTestCase testcase = new IsProvisionedTestCase();
- testcase.isProvisioned = isProvisioned;
- testcase.lp = new LinkProperties();
- testcase.lp.setLinkAddresses(lpAddrs);
- for (RouteInfo route : lpRoutes) {
- testcase.lp.addRoute(route);
- }
- for (InetAddress dns : lpDns) {
- testcase.lp.addDnsServer(dns);
- }
- testcase.config = config;
- return testcase;
- }
-
- @Test
- public void testInitialConfigurations() throws Exception {
- InitialConfigurationTestCase[] testcases = {
- validConf("valid IPv4 configuration",
- links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns("192.0.2.2")),
- validConf("another valid IPv4 configuration",
- links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns()),
- validConf("valid IPv6 configurations",
- links("2001:db8:dead:beef:f00::a0/64", "fe80::1/64"),
- prefixes("2001:db8:dead:beef::/64", "fe80::/64"),
- dns("2001:db8:dead:beef:f00::02")),
- validConf("valid IPv6 configurations",
- links("fe80::1/64"), prefixes("fe80::/64"), dns()),
- validConf("valid IPv6/v4 configuration",
- links("2001:db8:dead:beef:f00::a0/48", "192.0.2.12/24"),
- prefixes("2001:db8:dead:beef::/64", "192.0.2.0/24"),
- dns("192.0.2.2", "2001:db8:dead:beef:f00::02")),
- validConf("valid IPv6 configuration without any GUA.",
- links("fd00:1234:5678::1/48"),
- prefixes("fd00:1234:5678::/48"),
- dns("fd00:1234:5678::1000")),
-
- invalidConf("empty configuration", links(), prefixes(), dns()),
- invalidConf("v4 addr and dns not in any prefix",
- links("192.0.2.12/24"), prefixes("198.51.100.0/24"), dns("192.0.2.2")),
- invalidConf("v4 addr not in any prefix",
- links("198.51.2.12/24"), prefixes("198.51.100.0/24"), dns("192.0.2.2")),
- invalidConf("v4 dns addr not in any prefix",
- links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns("198.51.100.2")),
- invalidConf("v6 addr not in any prefix",
- links("2001:db8:dead:beef:f00::a0/64", "fe80::1/64"),
- prefixes("2001:db8:dead:beef::/64"),
- dns("2001:db8:dead:beef:f00::02")),
- invalidConf("v6 dns addr not in any prefix",
- links("fe80::1/64"), prefixes("fe80::/64"), dns("2001:db8:dead:beef:f00::02")),
- invalidConf("default ipv6 route and no GUA",
- links("fd01:1111:2222:3333::a0/128"), prefixes("::/0"), dns()),
- invalidConf("invalid v6 prefix length",
- links("2001:db8:dead:beef:f00::a0/128"), prefixes("2001:db8:dead:beef::/32"),
- dns()),
- invalidConf("another invalid v6 prefix length",
- links("2001:db8:dead:beef:f00::a0/128"), prefixes("2001:db8:dead:beef::/72"),
- dns())
- };
-
- for (InitialConfigurationTestCase testcase : testcases) {
- if (testcase.config.isValid() != testcase.isValid) {
- fail(testcase.errorMessage());
- }
- }
- }
-
- static class InitialConfigurationTestCase {
- String descr;
- boolean isValid;
- InitialConfiguration config;
- public String errorMessage() {
- return String.format("%s: expected configuration %s to be %s, but was %s",
- descr, config, validString(isValid), validString(!isValid));
- }
- static String validString(boolean isValid) {
- return isValid ? VALID : INVALID;
- }
- }
-
- static InitialConfigurationTestCase validConf(String descr, Set<LinkAddress> links,
- Set<IpPrefix> prefixes, Set<InetAddress> dns) {
- return confTestCase(descr, true, conf(links, prefixes, dns));
- }
-
- static InitialConfigurationTestCase invalidConf(String descr, Set<LinkAddress> links,
- Set<IpPrefix> prefixes, Set<InetAddress> dns) {
- return confTestCase(descr, false, conf(links, prefixes, dns));
- }
-
- static InitialConfigurationTestCase confTestCase(
- String descr, boolean isValid, InitialConfiguration config) {
- InitialConfigurationTestCase testcase = new InitialConfigurationTestCase();
- testcase.descr = descr;
- testcase.isValid = isValid;
- testcase.config = config;
- return testcase;
- }
-
- static LinkProperties linkproperties(Set<LinkAddress> addresses, Set<RouteInfo> routes) {
- LinkProperties lp = new LinkProperties();
- lp.setLinkAddresses(addresses);
- for (RouteInfo route : routes) {
- lp.addRoute(route);
- }
- return lp;
- }
-
- static InitialConfiguration conf(Set<LinkAddress> links, Set<IpPrefix> prefixes) {
- return conf(links, prefixes, new HashSet<>());
- }
-
- static InitialConfiguration conf(
- Set<LinkAddress> links, Set<IpPrefix> prefixes, Set<InetAddress> dns) {
- InitialConfiguration conf = new InitialConfiguration();
- conf.ipAddresses.addAll(links);
- conf.directlyConnectedRoutes.addAll(prefixes);
- conf.dnsServers.addAll(dns);
- return conf;
- }
-
- static Set<RouteInfo> routes(String... routes) {
- return mapIntoSet(routes, (r) -> new RouteInfo(new IpPrefix(r)));
- }
-
- static Set<IpPrefix> prefixes(String... prefixes) {
- return mapIntoSet(prefixes, IpPrefix::new);
- }
-
- static Set<LinkAddress> links(String... addresses) {
- return mapIntoSet(addresses, LinkAddress::new);
- }
-
- static Set<InetAddress> ips(String... addresses) {
- return mapIntoSet(addresses, InetAddress::getByName);
- }
-
- static Set<InetAddress> dns(String... addresses) {
- return ips(addresses);
- }
-
- static <A, B> Set<B> mapIntoSet(A[] in, Fn<A, B> fn) {
- Set<B> out = new HashSet<>(in.length);
- for (A item : in) {
- try {
- out.add(fn.call(item));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- return out;
- }
-
- interface Fn<A,B> {
- B call(A a) throws Exception;
- }
-
- @Test
- public void testAll() {
- List<String> list1 = Arrays.asList();
- List<String> list2 = Arrays.asList("foo");
- List<String> list3 = Arrays.asList("bar", "baz");
- List<String> list4 = Arrays.asList("foo", "bar", "baz");
-
- assertTrue(InitialConfiguration.all(list1, (x) -> false));
- assertFalse(InitialConfiguration.all(list2, (x) -> false));
- assertTrue(InitialConfiguration.all(list3, (x) -> true));
- assertTrue(InitialConfiguration.all(list2, (x) -> x.charAt(0) == 'f'));
- assertFalse(InitialConfiguration.all(list4, (x) -> x.charAt(0) == 'f'));
- }
-
- @Test
- public void testAny() {
- List<String> list1 = Arrays.asList();
- List<String> list2 = Arrays.asList("foo");
- List<String> list3 = Arrays.asList("bar", "baz");
- List<String> list4 = Arrays.asList("foo", "bar", "baz");
-
- assertFalse(InitialConfiguration.any(list1, (x) -> true));
- assertTrue(InitialConfiguration.any(list2, (x) -> true));
- assertTrue(InitialConfiguration.any(list2, (x) -> x.charAt(0) == 'f'));
- assertFalse(InitialConfiguration.any(list3, (x) -> x.charAt(0) == 'f'));
- assertTrue(InitialConfiguration.any(list4, (x) -> x.charAt(0) == 'f'));
- }
-
- @Test
- public void testFindAll() {
- List<String> list1 = Arrays.asList();
- List<String> list2 = Arrays.asList("foo");
- List<String> list3 = Arrays.asList("foo", "bar", "baz");
-
- assertEquals(list1, IpClient.findAll(list1, (x) -> true));
- assertEquals(list1, IpClient.findAll(list3, (x) -> false));
- assertEquals(list3, IpClient.findAll(list3, (x) -> true));
- assertEquals(list2, IpClient.findAll(list3, (x) -> x.charAt(0) == 'f'));
- }
-}
diff --git a/packages/NetworkStack/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.java b/packages/NetworkStack/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.java
deleted file mode 100644
index 64b168a..0000000
--- a/packages/NetworkStack/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.net.util.InterfaceParams;
-import android.net.util.SharedLog;
-import android.os.Handler;
-import android.os.Looper;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for IpReachabilityMonitor.
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class IpReachabilityMonitorTest {
-
- @Mock IpReachabilityMonitor.Callback mCallback;
- @Mock IpReachabilityMonitor.Dependencies mDependencies;
- @Mock SharedLog mLog;
- @Mock Context mContext;
- Handler mHandler;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mLog.forSubComponent(anyString())).thenReturn(mLog);
- mHandler = new Handler(Looper.getMainLooper());
- }
-
- IpReachabilityMonitor makeMonitor() {
- final InterfaceParams ifParams = new InterfaceParams("fake0", 1, null);
- return new IpReachabilityMonitor(
- mContext, ifParams, mHandler, mLog, mCallback, false, mDependencies);
- }
-
- @Test
- public void testNothing() {
- IpReachabilityMonitor monitor = makeMonitor();
- }
-}
diff --git a/packages/NetworkStack/tests/unit/src/android/net/util/ConnectivityPacketSummaryTest.java b/packages/NetworkStack/tests/unit/src/android/net/util/ConnectivityPacketSummaryTest.java
deleted file mode 100644
index 71be8b3..0000000
--- a/packages/NetworkStack/tests/unit/src/android/net/util/ConnectivityPacketSummaryTest.java
+++ /dev/null
@@ -1,419 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.net.MacAddress;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import libcore.util.HexEncoding;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for ConnectivityPacketSummary.
- *
- * @hide
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class ConnectivityPacketSummaryTest {
- private static final MacAddress MYHWADDR = MacAddress.fromString("80:7a:bf:6f:48:f3");
-
- private String getSummary(String hexBytes) {
- hexBytes = hexBytes.replaceAll("\\s+", "");
- final byte[] bytes = HexEncoding.decode(hexBytes.toCharArray(), false);
- return ConnectivityPacketSummary.summarize(MYHWADDR, bytes);
- }
-
- @Test
- public void testParseICMPv6DADProbe() {
- final String packet =
- // Ethernet
- "3333FF6F48F3 807ABF6F48F3 86DD" +
- // IPv6
- "600000000018 3A FF" +
- "00000000000000000000000000000000" +
- "FF0200000000000000000001FF6F48F3" +
- // ICMPv6
- "87 00 A8E7" +
- "00000000" +
- "FE80000000000000827ABFFFFE6F48F3";
-
- final String expected =
- "TX 80:7a:bf:6f:48:f3 > 33:33:ff:6f:48:f3 ipv6" +
- " :: > ff02::1:ff6f:48f3 icmp6" +
- " ns fe80::827a:bfff:fe6f:48f3";
-
- assertEquals(expected, getSummary(packet));
- }
-
- @Test
- public void testParseICMPv6RS() {
- final String packet =
- // Ethernet
- "333300000002 807ABF6F48F3 86DD" +
- // IPv6
- "600000000010 3A FF" +
- "FE80000000000000827ABFFFFE6F48F3" +
- "FF020000000000000000000000000002" +
- // ICMPv6 RS
- "85 00 6973" +
- "00000000" +
- "01 01 807ABF6F48F3";
-
- final String expected =
- "TX 80:7a:bf:6f:48:f3 > 33:33:00:00:00:02 ipv6" +
- " fe80::827a:bfff:fe6f:48f3 > ff02::2 icmp6" +
- " rs slla 80:7a:bf:6f:48:f3";
-
- assertEquals(expected, getSummary(packet));
- }
-
- @Test
- public void testParseICMPv6RA() {
- final String packet =
- // Ethernet
- "807ABF6F48F3 100E7E263FC1 86DD" +
- // IPv6
- "600000000068 3A FF" +
- "FE80000000000000FA000004FD000001" +
- "FE80000000000000827ABFFFFE6F48F3" +
- // ICMPv6 RA
- "86 00 8141" +
- "40 00 0E10" +
- "00000000" +
- "00000000" +
- "01 01 00005E000265" +
- "05 01 0000000005DC" +
- "19 05 000000000E10" +
- " 20014860486000000000000000008844" +
- " 20014860486000000000000000008888" +
- "03 04 40 C0" +
- " 00278D00" +
- " 00093A80" +
- " 00000000" +
- " 2401FA000004FD000000000000000000";
-
- final String expected =
- "RX 10:0e:7e:26:3f:c1 > 80:7a:bf:6f:48:f3 ipv6" +
- " fe80::fa00:4:fd00:1 > fe80::827a:bfff:fe6f:48f3 icmp6" +
- " ra slla 00:00:5e:00:02:65 mtu 1500";
-
- assertEquals(expected, getSummary(packet));
- }
-
- @Test
- public void testParseICMPv6NS() {
- final String packet =
- // Ethernet
- "807ABF6F48F3 100E7E263FC1 86DD" +
- // IPv6
- "6C0000000020 3A FF" +
- "FE80000000000000FA000004FD000001" +
- "FF0200000000000000000001FF01C146" +
- // ICMPv6 NS
- "87 00 8AD4" +
- "00000000" +
- "2401FA000004FD0015EA6A5C7B01C146" +
- "01 01 00005E000265";
-
- final String expected =
- "RX 10:0e:7e:26:3f:c1 > 80:7a:bf:6f:48:f3 ipv6" +
- " fe80::fa00:4:fd00:1 > ff02::1:ff01:c146 icmp6" +
- " ns 2401:fa00:4:fd00:15ea:6a5c:7b01:c146 slla 00:00:5e:00:02:65";
-
- assertEquals(expected, getSummary(packet));
- }
-
- @Test
- public void testInvalidICMPv6NDLength() {
- final String packet =
- // Ethernet
- "807ABF6F48F3 100E7E263FC1 86DD" +
- // IPv6
- "600000000068 3A FF" +
- "FE80000000000000FA000004FD000001" +
- "FE80000000000000827ABFFFFE6F48F3" +
- // ICMPv6 RA
- "86 00 8141" +
- "40 00 0E10" +
- "00000000" +
- "00000000" +
- "01 01 00005E000265" +
- "00 00 0102030405D6";
-
- final String expected =
- "RX 10:0e:7e:26:3f:c1 > 80:7a:bf:6f:48:f3 ipv6" +
- " fe80::fa00:4:fd00:1 > fe80::827a:bfff:fe6f:48f3 icmp6" +
- " ra slla 00:00:5e:00:02:65 <malformed>";
-
- assertEquals(expected, getSummary(packet));
- }
-
- @Test
- public void testParseICMPv6NA() {
- final String packet =
- // Ethernet
- "00005E000265 807ABF6F48F3 86DD" +
- "600000000020 3A FF" +
- "2401FA000004FD0015EA6A5C7B01C146" +
- "FE80000000000000FA000004FD000001" +
- "88 00 E8126" +
- "0000000" +
- "2401FA000004FD0015EA6A5C7B01C146" +
- "02 01 807ABF6F48F3";
-
- final String expected =
- "TX 80:7a:bf:6f:48:f3 > 00:00:5e:00:02:65 ipv6" +
- " 2401:fa00:4:fd00:15ea:6a5c:7b01:c146 > fe80::fa00:4:fd00:1 icmp6" +
- " na 2401:fa00:4:fd00:15ea:6a5c:7b01:c146 tlla 80:7a:bf:6f:48:f3";
-
- assertEquals(expected, getSummary(packet));
- }
-
- @Test
- public void testParseARPRequest() {
- final String packet =
- // Ethernet
- "FFFFFFFFFFFF 807ABF6F48F3 0806" +
- // ARP
- "0001 0800 06 04" +
- // Request
- "0001" +
- "807ABF6F48F3 64706ADB" +
- "000000000000 64706FFD";
-
- final String expected =
- "TX 80:7a:bf:6f:48:f3 > ff:ff:ff:ff:ff:ff arp" +
- " who-has 100.112.111.253";
-
- assertEquals(expected, getSummary(packet));
- }
-
- @Test
- public void testParseARPReply() {
- final String packet =
- // Ethernet
- "807ABF6F48F3 288A1CA8DFC1 0806" +
- // ARP
- "0001 0800 06 04" +
- // Reply
- "0002" +
- "288A1CA8DFC1 64706FFD"+
- "807ABF6F48F3 64706ADB" +
- // Ethernet padding to packet min size.
- "0000000000000000000000000000";
-
- final String expected =
- "RX 28:8a:1c:a8:df:c1 > 80:7a:bf:6f:48:f3 arp" +
- " reply 100.112.111.253 28:8a:1c:a8:df:c1";
-
- assertEquals(expected, getSummary(packet));
- }
-
- @Test
- public void testParseDHCPv4Discover() {
- final String packet =
- // Ethernet
- "FFFFFFFFFFFF 807ABF6F48F3 0800" +
- // IPv4
- "451001580000400040113986" +
- "00000000" +
- "FFFFFFFF" +
- // UDP
- "0044 0043" +
- "0144 5559" +
- // DHCPv4
- "01 01 06 00" +
- "79F7ACA4" +
- "0000 0000" +
- "00000000" +
- "00000000" +
- "00000000" +
- "00000000" +
- "807ABF6F48F300000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "63 82 53 63" +
- "35 01 01" +
- "3D 07 01807ABF6F48F3" +
- "39 02 05DC" +
- "3C 12 616E64726F69642D646863702D372E312E32" +
- "0C 18 616E64726F69642D36623030366333313333393835343139" +
- "37 0A 01 03 06 0F 1A 1C 33 3A 3B 2B" +
- "FF" +
- "00";
-
- final String expectedPrefix =
- "TX 80:7a:bf:6f:48:f3 > ff:ff:ff:ff:ff:ff ipv4" +
- " 0.0.0.0 > 255.255.255.255 udp" +
- " 68 > 67 dhcp4" +
- " 80:7a:bf:6f:48:f3 DISCOVER";
-
- assertTrue(getSummary(packet).startsWith(expectedPrefix));
- }
-
- @Test
- public void testParseDHCPv4Offer() {
- final String packet =
- // Ethernet
- "807ABF6F48F3 288A1CA8DFC1 0800" +
- // IPv4
- "4500013D4D2C0000401188CB" +
- "64706FFD" +
- "64706ADB" +
- // UDP
- "0043 0044" +
- "0129 371D" +
- // DHCPv4
- "02 01 06 01" +
- "79F7ACA4" +
- "0000 0000" +
- "00000000" +
- "64706ADB" +
- "00000000" +
- "00000000" +
- "807ABF6F48F300000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "63 82 53 63" +
- "35 01 02" +
- "36 04 AC188A0B" +
- "33 04 00000708" +
- "01 04 FFFFF000" +
- "03 04 64706FFE" +
- "06 08 08080808" +
- " 08080404" +
- "FF0001076165313A363636FF";
-
- final String expectedPrefix =
- "RX 28:8a:1c:a8:df:c1 > 80:7a:bf:6f:48:f3 ipv4" +
- " 100.112.111.253 > 100.112.106.219 udp" +
- " 67 > 68 dhcp4" +
- " 80:7a:bf:6f:48:f3 OFFER";
-
- assertTrue(getSummary(packet).startsWith(expectedPrefix));
- }
-
- @Test
- public void testParseDHCPv4Request() {
- final String packet =
- // Ethernet
- "FFFFFFFFFFFF 807ABF6F48F3 0800" +
- // IPv4
- "45100164000040004011397A" +
- "00000000" +
- "FFFFFFFF" +
- // UDP
- "0044 0043" +
- "0150 E5C7" +
- // DHCPv4
- "01 01 06 00" +
- "79F7ACA4" +
- "0001 0000" +
- "00000000" +
- "00000000" +
- "00000000" +
- "00000000" +
- "807ABF6F48F300000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "63 82 53 63" +
- "35 01 03" +
- "3D 07 01807ABF6F48F3" +
- "32 04 64706ADB" +
- "36 04 AC188A0B" +
- "39 02 05DC" +
- "3C 12 616E64726F69642D646863702D372E312E32" +
- "0C 18 616E64726F69642D36623030366333313333393835343139" +
- "37 0A 01 03 06 0F 1A 1C 33 3A 3B 2B" +
- "FF" +
- "00";
-
- final String expectedPrefix =
- "TX 80:7a:bf:6f:48:f3 > ff:ff:ff:ff:ff:ff ipv4" +
- " 0.0.0.0 > 255.255.255.255 udp" +
- " 68 > 67 dhcp4" +
- " 80:7a:bf:6f:48:f3 REQUEST";
-
- assertTrue(getSummary(packet).startsWith(expectedPrefix));
- }
-
- @Test
- public void testParseDHCPv4Ack() {
- final String packet =
- // Ethernet
- "807ABF6F48F3 288A1CA8DFC1 0800" +
- // IPv4
- "4500013D4D3B0000401188BC" +
- "64706FFD" +
- "64706ADB" +
- // UDP
- "0043 0044" +
- "0129 341C" +
- // DHCPv4
- "02 01 06 01" +
- "79F7ACA4" +
- "0001 0000" +
- "00000000" +
- "64706ADB" +
- "00000000" +
- "00000000" +
- "807ABF6F48F300000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "0000000000000000000000000000000000000000000000000000000000000000" +
- "63 82 53 63" +
- "35 01 05" +
- "36 04 AC188A0B" +
- "33 04 00000708" +
- "01 04 FFFFF000" +
- "03 04 64706FFE" +
- "06 08 08080808" +
- " 08080404" +
- "FF0001076165313A363636FF";
-
- final String expectedPrefix =
- "RX 28:8a:1c:a8:df:c1 > 80:7a:bf:6f:48:f3 ipv4" +
- " 100.112.111.253 > 100.112.106.219 udp" +
- " 67 > 68 dhcp4" +
- " 80:7a:bf:6f:48:f3 ACK";
-
- assertTrue(getSummary(packet).startsWith(expectedPrefix));
- }
-}
diff --git a/packages/NetworkStack/tests/unit/src/android/net/util/PacketReaderTest.java b/packages/NetworkStack/tests/unit/src/android/net/util/PacketReaderTest.java
deleted file mode 100644
index 289dcad..0000000
--- a/packages/NetworkStack/tests/unit/src/android/net/util/PacketReaderTest.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import static android.net.util.PacketReader.DEFAULT_RECV_BUF_SIZE;
-import static android.system.OsConstants.AF_INET6;
-import static android.system.OsConstants.IPPROTO_UDP;
-import static android.system.OsConstants.SOCK_DGRAM;
-import static android.system.OsConstants.SOCK_NONBLOCK;
-import static android.system.OsConstants.SOL_SOCKET;
-import static android.system.OsConstants.SO_SNDTIMEO;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.StructTimeval;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.FileDescriptor;
-import java.net.DatagramPacket;
-import java.net.DatagramSocket;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.SocketException;
-import java.util.Arrays;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Tests for PacketReader.
- *
- * @hide
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class PacketReaderTest {
- static final InetAddress LOOPBACK6 = Inet6Address.getLoopbackAddress();
- static final StructTimeval TIMEO = StructTimeval.fromMillis(500);
-
- protected CountDownLatch mLatch;
- protected FileDescriptor mLocalSocket;
- protected InetSocketAddress mLocalSockName;
- protected byte[] mLastRecvBuf;
- protected boolean mStopped;
- protected HandlerThread mHandlerThread;
- protected PacketReader mReceiver;
-
- class UdpLoopbackReader extends PacketReader {
- public UdpLoopbackReader(Handler h) {
- super(h);
- }
-
- @Override
- protected FileDescriptor createFd() {
- FileDescriptor s = null;
- try {
- s = Os.socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
- Os.bind(s, LOOPBACK6, 0);
- mLocalSockName = (InetSocketAddress) Os.getsockname(s);
- Os.setsockoptTimeval(s, SOL_SOCKET, SO_SNDTIMEO, TIMEO);
- } catch (ErrnoException|SocketException e) {
- closeFd(s);
- fail();
- return null;
- }
-
- mLocalSocket = s;
- return s;
- }
-
- @Override
- protected void handlePacket(byte[] recvbuf, int length) {
- mLastRecvBuf = Arrays.copyOf(recvbuf, length);
- mLatch.countDown();
- }
-
- @Override
- protected void onStart() {
- mStopped = false;
- mLatch.countDown();
- }
-
- @Override
- protected void onStop() {
- mStopped = true;
- mLatch.countDown();
- }
- };
-
- @Before
- public void setUp() {
- resetLatch();
- mLocalSocket = null;
- mLocalSockName = null;
- mLastRecvBuf = null;
- mStopped = false;
-
- mHandlerThread = new HandlerThread(PacketReaderTest.class.getSimpleName());
- mHandlerThread.start();
- }
-
- @After
- public void tearDown() throws Exception {
- if (mReceiver != null) {
- mHandlerThread.getThreadHandler().post(() -> { mReceiver.stop(); });
- waitForActivity();
- }
- mReceiver = null;
- mHandlerThread.quit();
- mHandlerThread = null;
- }
-
- void resetLatch() { mLatch = new CountDownLatch(1); }
-
- void waitForActivity() throws Exception {
- try {
- mLatch.await(1000, TimeUnit.MILLISECONDS);
- } finally {
- resetLatch();
- }
- }
-
- void sendPacket(byte[] contents) throws Exception {
- final DatagramSocket sender = new DatagramSocket();
- sender.connect(mLocalSockName);
- sender.send(new DatagramPacket(contents, contents.length));
- sender.close();
- }
-
- @Test
- public void testBasicWorking() throws Exception {
- final Handler h = mHandlerThread.getThreadHandler();
- mReceiver = new UdpLoopbackReader(h);
-
- h.post(() -> { mReceiver.start(); });
- waitForActivity();
- assertTrue(mLocalSockName != null);
- assertEquals(LOOPBACK6, mLocalSockName.getAddress());
- assertTrue(0 < mLocalSockName.getPort());
- assertTrue(mLocalSocket != null);
- assertFalse(mStopped);
-
- final byte[] one = "one 1".getBytes("UTF-8");
- sendPacket(one);
- waitForActivity();
- assertEquals(1, mReceiver.numPacketsReceived());
- assertTrue(Arrays.equals(one, mLastRecvBuf));
- assertFalse(mStopped);
-
- final byte[] two = "two 2".getBytes("UTF-8");
- sendPacket(two);
- waitForActivity();
- assertEquals(2, mReceiver.numPacketsReceived());
- assertTrue(Arrays.equals(two, mLastRecvBuf));
- assertFalse(mStopped);
-
- mReceiver.stop();
- waitForActivity();
- assertEquals(2, mReceiver.numPacketsReceived());
- assertTrue(Arrays.equals(two, mLastRecvBuf));
- assertTrue(mStopped);
- mReceiver = null;
- }
-
- class NullPacketReader extends PacketReader {
- public NullPacketReader(Handler h, int recvbufsize) {
- super(h, recvbufsize);
- }
-
- @Override
- public FileDescriptor createFd() { return null; }
- }
-
- @Test
- public void testMinimalRecvBufSize() throws Exception {
- final Handler h = mHandlerThread.getThreadHandler();
-
- for (int i : new int[]{-1, 0, 1, DEFAULT_RECV_BUF_SIZE-1}) {
- final PacketReader b = new NullPacketReader(h, i);
- assertEquals(DEFAULT_RECV_BUF_SIZE, b.recvBufSize());
- }
- }
-}
diff --git a/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
deleted file mode 100644
index 262641d..0000000
--- a/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ /dev/null
@@ -1,1161 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.connectivity;
-
-import static android.net.CaptivePortal.APP_RETURN_DISMISSED;
-import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
-import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK;
-import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP;
-import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS;
-import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
-import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
-import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
-import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD;
-import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_EVALUATION_TYPE;
-import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL;
-import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD;
-import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_DNS;
-import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS;
-import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS;
-import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.annotation.NonNull;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.net.ConnectivityManager;
-import android.net.DnsResolver;
-import android.net.INetworkMonitorCallbacks;
-import android.net.InetAddresses;
-import android.net.LinkProperties;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.captiveportal.CaptivePortalProbeResult;
-import android.net.metrics.IpConnectivityLog;
-import android.net.shared.PrivateDnsConfig;
-import android.net.util.SharedLog;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
-import android.os.Bundle;
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.telephony.CellSignalStrength;
-import android.telephony.TelephonyManager;
-import android.util.ArrayMap;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.networkstack.R;
-import com.android.networkstack.metrics.DataStallDetectionStats;
-import com.android.networkstack.metrics.DataStallStatsUtils;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
-import org.mockito.verification.VerificationWithTimeout;
-
-import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.net.InetAddress;
-import java.net.URL;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Random;
-import java.util.concurrent.Executor;
-
-import javax.net.ssl.SSLHandshakeException;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class NetworkMonitorTest {
- private static final String LOCATION_HEADER = "location";
-
- private @Mock Context mContext;
- private @Mock Resources mResources;
- private @Mock IpConnectivityLog mLogger;
- private @Mock SharedLog mValidationLogger;
- private @Mock NetworkInfo mNetworkInfo;
- private @Mock DnsResolver mDnsResolver;
- private @Mock ConnectivityManager mCm;
- private @Mock TelephonyManager mTelephony;
- private @Mock WifiManager mWifi;
- private @Mock HttpURLConnection mHttpConnection;
- private @Mock HttpURLConnection mHttpsConnection;
- private @Mock HttpURLConnection mFallbackConnection;
- private @Mock HttpURLConnection mOtherFallbackConnection;
- private @Mock Random mRandom;
- private @Mock NetworkMonitor.Dependencies mDependencies;
- private @Mock INetworkMonitorCallbacks mCallbacks;
- private @Spy Network mCleartextDnsNetwork = new Network(TEST_NETID);
- private @Mock Network mNetwork;
- private @Mock DataStallStatsUtils mDataStallStatsUtils;
- private @Mock WifiInfo mWifiInfo;
- private @Captor ArgumentCaptor<String> mNetworkTestedRedirectUrlCaptor;
-
- private HashSet<WrappedNetworkMonitor> mCreatedNetworkMonitors;
- private HashSet<BroadcastReceiver> mRegisteredReceivers;
-
- private static final int TEST_NETID = 4242;
- private static final String TEST_HTTP_URL = "http://www.google.com/gen_204";
- private static final String TEST_HTTPS_URL = "https://www.google.com/gen_204";
- private static final String TEST_FALLBACK_URL = "http://fallback.google.com/gen_204";
- private static final String TEST_OTHER_FALLBACK_URL = "http://otherfallback.google.com/gen_204";
- private static final String TEST_MCCMNC = "123456";
-
- private static final int VALIDATION_RESULT_INVALID = 0;
- private static final int VALIDATION_RESULT_PORTAL = 0;
- private static final String TEST_REDIRECT_URL = "android.com";
- private static final int VALIDATION_RESULT_PARTIAL = NETWORK_VALIDATION_PROBE_DNS
- | NETWORK_VALIDATION_PROBE_HTTP
- | NETWORK_VALIDATION_RESULT_PARTIAL;
- private static final int VALIDATION_RESULT_FALLBACK_PARTIAL = NETWORK_VALIDATION_PROBE_DNS
- | NETWORK_VALIDATION_PROBE_FALLBACK
- | NETWORK_VALIDATION_RESULT_PARTIAL;
- private static final int VALIDATION_RESULT_VALID = NETWORK_VALIDATION_PROBE_DNS
- | NETWORK_VALIDATION_PROBE_HTTPS
- | NETWORK_VALIDATION_RESULT_VALID;
-
- private static final int RETURN_CODE_DNS_SUCCESS = 0;
- private static final int RETURN_CODE_DNS_TIMEOUT = 255;
- private static final int DEFAULT_DNS_TIMEOUT_THRESHOLD = 5;
-
- private static final int HANDLER_TIMEOUT_MS = 1000;
-
- private static final LinkProperties TEST_LINK_PROPERTIES = new LinkProperties();
-
- private static final NetworkCapabilities METERED_CAPABILITIES = new NetworkCapabilities()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .addCapability(NET_CAPABILITY_INTERNET);
-
- private static final NetworkCapabilities NOT_METERED_CAPABILITIES = new NetworkCapabilities()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .addCapability(NET_CAPABILITY_INTERNET)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
-
- private static final NetworkCapabilities NO_INTERNET_CAPABILITIES = new NetworkCapabilities()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
-
- /**
- * Fakes DNS responses.
- *
- * Allows test methods to configure the IP addresses that will be resolved by
- * Network#getAllByName and by DnsResolver#query.
- */
- class FakeDns {
- private final ArrayMap<String, List<InetAddress>> mAnswers = new ArrayMap<>();
- private boolean mNonBypassPrivateDnsWorking = true;
-
- /** Whether DNS queries on mNonBypassPrivateDnsWorking should succeed. */
- private void setNonBypassPrivateDnsWorking(boolean working) {
- mNonBypassPrivateDnsWorking = working;
- }
-
- /** Clears all DNS entries. */
- private synchronized void clearAll() {
- mAnswers.clear();
- }
-
- /** Returns the answer for a given name on the given mock network. */
- private synchronized List<InetAddress> getAnswer(Object mock, String hostname) {
- if (mock == mNetwork && !mNonBypassPrivateDnsWorking) {
- return null;
- }
- if (mAnswers.containsKey(hostname)) {
- return mAnswers.get(hostname);
- }
- return mAnswers.get("*");
- }
-
- /** Sets the answer for a given name. */
- private synchronized void setAnswer(String hostname, String[] answer)
- throws UnknownHostException {
- if (answer == null) {
- mAnswers.remove(hostname);
- } else {
- List<InetAddress> answerList = new ArrayList<>();
- for (String addr : answer) {
- answerList.add(InetAddresses.parseNumericAddress(addr));
- }
- mAnswers.put(hostname, answerList);
- }
- }
-
- /** Simulates a getAllByName call for the specified name on the specified mock network. */
- private InetAddress[] getAllByName(Object mock, String hostname)
- throws UnknownHostException {
- List<InetAddress> answer = getAnswer(mock, hostname);
- if (answer == null || answer.size() == 0) {
- throw new UnknownHostException(hostname);
- }
- return answer.toArray(new InetAddress[0]);
- }
-
- /** Starts mocking DNS queries. */
- private void startMocking() throws UnknownHostException {
- // Queries on mNetwork using getAllByName.
- doAnswer(invocation -> {
- return getAllByName(invocation.getMock(), invocation.getArgument(0));
- }).when(mNetwork).getAllByName(any());
-
- // Queries on mCleartextDnsNetwork using DnsResolver#query.
- doAnswer(invocation -> {
- String hostname = (String) invocation.getArgument(1);
- Executor executor = (Executor) invocation.getArgument(3);
- DnsResolver.Callback<List<InetAddress>> callback = invocation.getArgument(5);
-
- List<InetAddress> answer = getAnswer(invocation.getMock(), hostname);
- if (answer != null && answer.size() > 0) {
- new Handler(Looper.getMainLooper()).post(() -> {
- executor.execute(() -> callback.onAnswer(answer, 0));
- });
- }
- // If no answers, do nothing. sendDnsProbeWithTimeout will time out and throw UHE.
- return null;
- }).when(mDnsResolver).query(any(), any(), anyInt(), any(), any(), any());
-
- // Queries on mCleartextDnsNetwork using using DnsResolver#query with QueryType.
- doAnswer(invocation -> {
- String hostname = (String) invocation.getArgument(1);
- Executor executor = (Executor) invocation.getArgument(4);
- DnsResolver.Callback<List<InetAddress>> callback = invocation.getArgument(6);
-
- List<InetAddress> answer = getAnswer(invocation.getMock(), hostname);
- if (answer != null && answer.size() > 0) {
- new Handler(Looper.getMainLooper()).post(() -> {
- executor.execute(() -> callback.onAnswer(answer, 0));
- });
- }
- // If no answers, do nothing. sendDnsProbeWithTimeout will time out and throw UHE.
- return null;
- }).when(mDnsResolver).query(any(), any(), anyInt(), anyInt(), any(), any(), any());
- }
- }
-
- private FakeDns mFakeDns;
-
- @Before
- public void setUp() throws IOException {
- MockitoAnnotations.initMocks(this);
- when(mDependencies.getPrivateDnsBypassNetwork(any())).thenReturn(mCleartextDnsNetwork);
- when(mDependencies.getDnsResolver()).thenReturn(mDnsResolver);
- when(mDependencies.getRandom()).thenReturn(mRandom);
- when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt()))
- .thenReturn(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
- when(mDependencies.getDeviceConfigPropertyInt(any(), eq(CAPTIVE_PORTAL_USE_HTTPS),
- anyInt())).thenReturn(1);
- when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL), any()))
- .thenReturn(TEST_HTTP_URL);
- when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTPS_URL), any()))
- .thenReturn(TEST_HTTPS_URL);
-
- doReturn(mCleartextDnsNetwork).when(mNetwork).getPrivateDnsBypassingCopy();
-
- when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mCm);
- when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephony);
- when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifi);
- when(mContext.getResources()).thenReturn(mResources);
-
- when(mResources.getString(anyInt())).thenReturn("");
- when(mResources.getStringArray(anyInt())).thenReturn(new String[0]);
-
- when(mNetworkInfo.getType()).thenReturn(ConnectivityManager.TYPE_WIFI);
- setFallbackUrl(TEST_FALLBACK_URL);
- setOtherFallbackUrls(TEST_OTHER_FALLBACK_URL);
- setFallbackSpecs(null); // Test with no fallback spec by default
- when(mRandom.nextInt()).thenReturn(0);
-
- // DNS probe timeout should not be defined more than half of HANDLER_TIMEOUT_MS. Otherwise,
- // it will fail the test because of timeout expired for querying AAAA and A sequentially.
- when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout)))
- .thenReturn(200);
-
- doAnswer((invocation) -> {
- URL url = invocation.getArgument(0);
- switch(url.toString()) {
- case TEST_HTTP_URL:
- return mHttpConnection;
- case TEST_HTTPS_URL:
- return mHttpsConnection;
- case TEST_FALLBACK_URL:
- return mFallbackConnection;
- case TEST_OTHER_FALLBACK_URL:
- return mOtherFallbackConnection;
- default:
- fail("URL not mocked: " + url.toString());
- return null;
- }
- }).when(mCleartextDnsNetwork).openConnection(any());
- when(mHttpConnection.getRequestProperties()).thenReturn(new ArrayMap<>());
- when(mHttpsConnection.getRequestProperties()).thenReturn(new ArrayMap<>());
-
- mFakeDns = new FakeDns();
- mFakeDns.startMocking();
- mFakeDns.setAnswer("*", new String[]{"2001:db8::1", "192.0.2.2"});
-
- when(mContext.registerReceiver(any(BroadcastReceiver.class), any())).then((invocation) -> {
- mRegisteredReceivers.add(invocation.getArgument(0));
- return new Intent();
- });
-
- doAnswer((invocation) -> {
- mRegisteredReceivers.remove(invocation.getArgument(0));
- return null;
- }).when(mContext).unregisterReceiver(any());
-
- setMinDataStallEvaluateInterval(500);
- setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS);
- setValidDataStallDnsTimeThreshold(500);
- setConsecutiveDnsTimeoutThreshold(5);
-
- mCreatedNetworkMonitors = new HashSet<>();
- mRegisteredReceivers = new HashSet<>();
- }
-
- @After
- public void tearDown() {
- mFakeDns.clearAll();
- assertTrue(mCreatedNetworkMonitors.size() > 0);
- // Make a local copy of mCreatedNetworkMonitors because during the iteration below,
- // WrappedNetworkMonitor#onQuitting will delete elements from it on the handler threads.
- WrappedNetworkMonitor[] networkMonitors = mCreatedNetworkMonitors.toArray(
- new WrappedNetworkMonitor[0]);
- for (WrappedNetworkMonitor nm : networkMonitors) {
- nm.notifyNetworkDisconnected();
- nm.awaitQuit();
- }
- assertEquals("NetworkMonitor still running after disconnect",
- 0, mCreatedNetworkMonitors.size());
- assertEquals("BroadcastReceiver still registered after disconnect",
- 0, mRegisteredReceivers.size());
- }
-
- private class WrappedNetworkMonitor extends NetworkMonitor {
- private long mProbeTime = 0;
- private final ConditionVariable mQuitCv = new ConditionVariable(false);
-
- WrappedNetworkMonitor() {
- super(mContext, mCallbacks, mNetwork, mLogger, mValidationLogger,
- mDependencies, mDataStallStatsUtils);
- }
-
- @Override
- protected long getLastProbeTime() {
- return mProbeTime;
- }
-
- protected void setLastProbeTime(long time) {
- mProbeTime = time;
- }
-
- @Override
- protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) {
- generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
- }
-
- @Override
- protected void onQuitting() {
- assertTrue(mCreatedNetworkMonitors.remove(this));
- mQuitCv.open();
- }
-
- protected void awaitQuit() {
- assertTrue("NetworkMonitor did not quit after " + HANDLER_TIMEOUT_MS + "ms",
- mQuitCv.block(HANDLER_TIMEOUT_MS));
- }
- }
-
- private WrappedNetworkMonitor makeMonitor(NetworkCapabilities nc) {
- final WrappedNetworkMonitor nm = new WrappedNetworkMonitor();
- nm.start();
- setNetworkCapabilities(nm, nc);
- waitForIdle(nm.getHandler());
- mCreatedNetworkMonitors.add(nm);
- return nm;
- }
-
- private WrappedNetworkMonitor makeMeteredNetworkMonitor() {
- final WrappedNetworkMonitor nm = makeMonitor(METERED_CAPABILITIES);
- return nm;
- }
-
- private WrappedNetworkMonitor makeNotMeteredNetworkMonitor() {
- final WrappedNetworkMonitor nm = makeMonitor(NOT_METERED_CAPABILITIES);
- return nm;
- }
-
- private void setNetworkCapabilities(NetworkMonitor nm, NetworkCapabilities nc) {
- nm.notifyNetworkCapabilitiesChanged(nc);
- waitForIdle(nm.getHandler());
- }
-
- private void waitForIdle(Handler handler) {
- final ConditionVariable cv = new ConditionVariable(false);
- handler.post(cv::open);
- if (!cv.block(HANDLER_TIMEOUT_MS)) {
- fail("Timed out waiting for handler");
- }
- }
-
- @Test
- public void testGetIntSetting() throws Exception {
- WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor();
-
- // No config resource, no device config. Expect to get default resource.
- doThrow(new Resources.NotFoundException())
- .when(mResources).getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout));
- doAnswer(invocation -> {
- int defaultValue = invocation.getArgument(2);
- return defaultValue;
- }).when(mDependencies).getDeviceConfigPropertyInt(any(),
- eq(NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT),
- anyInt());
- when(mResources.getInteger(eq(R.integer.default_captive_portal_dns_probe_timeout)))
- .thenReturn(42);
- assertEquals(42, wnm.getIntSetting(mContext,
- R.integer.config_captive_portal_dns_probe_timeout,
- NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT,
- R.integer.default_captive_portal_dns_probe_timeout));
-
- // Set device config. Expect to get device config.
- when(mDependencies.getDeviceConfigPropertyInt(any(),
- eq(NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT), anyInt()))
- .thenReturn(1234);
- assertEquals(1234, wnm.getIntSetting(mContext,
- R.integer.config_captive_portal_dns_probe_timeout,
- NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT,
- R.integer.default_captive_portal_dns_probe_timeout));
-
- // Set config resource. Expect to get config resource.
- when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout)))
- .thenReturn(5678);
- assertEquals(5678, wnm.getIntSetting(mContext,
- R.integer.config_captive_portal_dns_probe_timeout,
- NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT,
- R.integer.default_captive_portal_dns_probe_timeout));
- }
-
- @Test
- public void testIsCaptivePortal_HttpProbeIsPortal() throws IOException {
- setSslException(mHttpsConnection);
- setPortal302(mHttpConnection);
- runPortalNetworkTest(VALIDATION_RESULT_PORTAL);
- }
-
- @Test
- public void testIsCaptivePortal_HttpsProbeIsNotPortal() throws IOException {
- setStatus(mHttpsConnection, 204);
- setStatus(mHttpConnection, 500);
-
- runNotPortalNetworkTest();
- }
-
- @Test
- public void testIsCaptivePortal_FallbackProbeIsPortal() throws IOException {
- setSslException(mHttpsConnection);
- setStatus(mHttpConnection, 500);
- setPortal302(mFallbackConnection);
- runPortalNetworkTest(VALIDATION_RESULT_INVALID);
- }
-
- @Test
- public void testIsCaptivePortal_FallbackProbeIsNotPortal() throws IOException {
- setSslException(mHttpsConnection);
- setStatus(mHttpConnection, 500);
- setStatus(mFallbackConnection, 500);
-
- // Fallback probe did not see portal, HTTPS failed -> inconclusive
- runFailedNetworkTest();
- }
-
- @Test
- public void testIsCaptivePortal_OtherFallbackProbeIsPortal() throws IOException {
- // Set all fallback probes but one to invalid URLs to verify they are being skipped
- setFallbackUrl(TEST_FALLBACK_URL);
- setOtherFallbackUrls(TEST_FALLBACK_URL + "," + TEST_OTHER_FALLBACK_URL);
-
- setSslException(mHttpsConnection);
- setStatus(mHttpConnection, 500);
- setStatus(mFallbackConnection, 500);
- setPortal302(mOtherFallbackConnection);
-
- // TEST_OTHER_FALLBACK_URL is third
- when(mRandom.nextInt()).thenReturn(2);
-
- // First check always uses the first fallback URL: inconclusive
- final NetworkMonitor monitor = runNetworkTest(VALIDATION_RESULT_INVALID);
- assertNull(mNetworkTestedRedirectUrlCaptor.getValue());
- verify(mFallbackConnection, times(1)).getResponseCode();
- verify(mOtherFallbackConnection, never()).getResponseCode();
-
- // Second check uses the URL chosen by Random
- final CaptivePortalProbeResult result = monitor.isCaptivePortal();
- assertTrue(result.isPortal());
- verify(mOtherFallbackConnection, times(1)).getResponseCode();
- }
-
- @Test
- public void testIsCaptivePortal_AllProbesFailed() throws IOException {
- setSslException(mHttpsConnection);
- setStatus(mHttpConnection, 500);
- setStatus(mFallbackConnection, 404);
-
- runFailedNetworkTest();
- verify(mFallbackConnection, times(1)).getResponseCode();
- verify(mOtherFallbackConnection, never()).getResponseCode();
- }
-
- @Test
- public void testIsCaptivePortal_InvalidUrlSkipped() throws IOException {
- setFallbackUrl("invalid");
- setOtherFallbackUrls("otherinvalid," + TEST_OTHER_FALLBACK_URL + ",yetanotherinvalid");
-
- setSslException(mHttpsConnection);
- setStatus(mHttpConnection, 500);
- setPortal302(mOtherFallbackConnection);
- runPortalNetworkTest(VALIDATION_RESULT_INVALID);
- verify(mOtherFallbackConnection, times(1)).getResponseCode();
- verify(mFallbackConnection, never()).getResponseCode();
- }
-
- private void setupFallbackSpec() throws IOException {
- setFallbackSpecs("http://example.com@@/@@204@@/@@"
- + "@@,@@"
- + TEST_OTHER_FALLBACK_URL + "@@/@@30[12]@@/@@https://(www\\.)?google.com/?.*");
-
- setSslException(mHttpsConnection);
- setStatus(mHttpConnection, 500);
-
- // Use the 2nd fallback spec
- when(mRandom.nextInt()).thenReturn(1);
- }
-
- @Test
- public void testIsCaptivePortal_FallbackSpecIsPartial() throws IOException {
- setupFallbackSpec();
- set302(mOtherFallbackConnection, "https://www.google.com/test?q=3");
-
- // HTTPS failed, fallback spec went through -> partial connectivity
- runPartialConnectivityNetworkTest(VALIDATION_RESULT_FALLBACK_PARTIAL);
- verify(mOtherFallbackConnection, times(1)).getResponseCode();
- verify(mFallbackConnection, never()).getResponseCode();
- }
-
- @Test
- public void testIsCaptivePortal_FallbackSpecIsPortal() throws IOException {
- setupFallbackSpec();
- set302(mOtherFallbackConnection, "http://login.portal.example.com");
- runPortalNetworkTest(VALIDATION_RESULT_INVALID);
- }
-
- @Test
- public void testIsCaptivePortal_IgnorePortals() throws IOException {
- setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE);
- setSslException(mHttpsConnection);
- setPortal302(mHttpConnection);
-
- runNoValidationNetworkTest();
- }
-
- @Test
- public void testIsDataStall_EvaluationDisabled() {
- setDataStallEvaluationType(0);
- WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor();
- wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
- assertFalse(wrappedMonitor.isDataStall());
- }
-
- @Test
- public void testIsDataStall_EvaluationDnsOnNotMeteredNetwork() {
- WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor();
- wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
- makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
- assertTrue(wrappedMonitor.isDataStall());
- }
-
- @Test
- public void testIsDataStall_EvaluationDnsOnMeteredNetwork() {
- WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor();
- wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
- assertFalse(wrappedMonitor.isDataStall());
-
- wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
- makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
- assertTrue(wrappedMonitor.isDataStall());
- }
-
- @Test
- public void testIsDataStall_EvaluationDnsWithDnsTimeoutCount() {
- WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor();
- wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
- makeDnsTimeoutEvent(wrappedMonitor, 3);
- assertFalse(wrappedMonitor.isDataStall());
- // Reset consecutive timeout counts.
- makeDnsSuccessEvent(wrappedMonitor, 1);
- makeDnsTimeoutEvent(wrappedMonitor, 2);
- assertFalse(wrappedMonitor.isDataStall());
-
- makeDnsTimeoutEvent(wrappedMonitor, 3);
- assertTrue(wrappedMonitor.isDataStall());
-
- // Set the value to larger than the default dns log size.
- setConsecutiveDnsTimeoutThreshold(51);
- wrappedMonitor = makeMeteredNetworkMonitor();
- wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
- makeDnsTimeoutEvent(wrappedMonitor, 50);
- assertFalse(wrappedMonitor.isDataStall());
-
- makeDnsTimeoutEvent(wrappedMonitor, 1);
- assertTrue(wrappedMonitor.isDataStall());
- }
-
- @Test
- public void testIsDataStall_EvaluationDnsWithDnsTimeThreshold() {
- // Test dns events happened in valid dns time threshold.
- WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor();
- wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
- makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
- assertFalse(wrappedMonitor.isDataStall());
- wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
- assertTrue(wrappedMonitor.isDataStall());
-
- // Test dns events happened before valid dns time threshold.
- setValidDataStallDnsTimeThreshold(0);
- wrappedMonitor = makeMeteredNetworkMonitor();
- wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100);
- makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD);
- assertFalse(wrappedMonitor.isDataStall());
- wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
- assertFalse(wrappedMonitor.isDataStall());
- }
-
- @Test
- public void testBrokenNetworkNotValidated() throws Exception {
- setSslException(mHttpsConnection);
- setStatus(mHttpConnection, 500);
- setStatus(mFallbackConnection, 404);
-
- runFailedNetworkTest();
- }
-
- @Test
- public void testNoInternetCapabilityValidated() throws Exception {
- runNetworkTest(NO_INTERNET_CAPABILITIES, NETWORK_VALIDATION_RESULT_VALID,
- getGeneralVerification());
- verify(mCleartextDnsNetwork, never()).openConnection(any());
- }
-
- @Test
- public void testLaunchCaptivePortalApp() throws Exception {
- setSslException(mHttpsConnection);
- setPortal302(mHttpConnection);
-
- final NetworkMonitor nm = makeMonitor(METERED_CAPABILITIES);
- nm.notifyNetworkConnected(TEST_LINK_PROPERTIES, METERED_CAPABILITIES);
-
- verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
- .showProvisioningNotification(any(), any());
-
- assertEquals(1, mRegisteredReceivers.size());
-
- // Check that startCaptivePortalApp sends the expected intent.
- nm.launchCaptivePortalApp();
-
- final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
- final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class);
- verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1))
- .startCaptivePortalApp(networkCaptor.capture(), bundleCaptor.capture());
- final Bundle bundle = bundleCaptor.getValue();
- final Network bundleNetwork = bundle.getParcelable(ConnectivityManager.EXTRA_NETWORK);
- assertEquals(TEST_NETID, bundleNetwork.netId);
- // network is passed both in bundle and as parameter, as the bundle is opaque to the
- // framework and only intended for the captive portal app, but the framework needs
- // the network to identify the right NetworkMonitor.
- assertEquals(TEST_NETID, networkCaptor.getValue().netId);
-
- // Have the app report that the captive portal is dismissed, and check that we revalidate.
- setStatus(mHttpsConnection, 204);
- setStatus(mHttpConnection, 204);
-
- reset(mCallbacks);
- nm.notifyCaptivePortalAppFinished(APP_RETURN_DISMISSED);
- verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce())
- .notifyNetworkTested(eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP
- | NETWORK_VALIDATION_RESULT_VALID), any());
- assertEquals(0, mRegisteredReceivers.size());
- }
-
- @Test
- public void testPrivateDnsSuccess() throws Exception {
- setStatus(mHttpsConnection, 204);
- setStatus(mHttpConnection, 204);
- mFakeDns.setAnswer("dns.google", new String[]{"2001:db8::53"});
-
- WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor();
- wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0]));
- wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, NOT_METERED_CAPABILITIES);
- verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
- .notifyNetworkTested(eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_PRIVDNS),
- eq(null));
- }
-
- @Test
- public void testPrivateDnsResolutionRetryUpdate() throws Exception {
- // Set a private DNS hostname that doesn't resolve and expect validation to fail.
- mFakeDns.setAnswer("dns.google", new String[0]);
- setStatus(mHttpsConnection, 204);
- setStatus(mHttpConnection, 204);
-
- WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor();
- wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0]));
- wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, NOT_METERED_CAPABILITIES);
- verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce())
- .notifyNetworkTested(
- eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS),
- eq(null));
-
- // Fix DNS and retry, expect validation to succeed.
- reset(mCallbacks);
- mFakeDns.setAnswer("dns.google", new String[]{"2001:db8::1"});
-
- wnm.forceReevaluation(Process.myUid());
- verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce())
- .notifyNetworkTested(eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_PRIVDNS),
- eq(null));
-
- // Change configuration to an invalid DNS name, expect validation to fail.
- reset(mCallbacks);
- mFakeDns.setAnswer("dns.bad", new String[0]);
- wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.bad", new InetAddress[0]));
- // Strict mode hostname resolve fail. Expect only notification for evaluation fail. No probe
- // notification.
- verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
- .notifyNetworkTested(eq(VALIDATION_RESULT_VALID), eq(null));
-
- // Change configuration back to working again, but make private DNS not work.
- // Expect validation to fail.
- reset(mCallbacks);
- mFakeDns.setNonBypassPrivateDnsWorking(false);
- wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google",
- new InetAddress[0]));
- verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce())
- .notifyNetworkTested(
- eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS),
- eq(null));
-
- // Make private DNS work again. Expect validation to succeed.
- reset(mCallbacks);
- mFakeDns.setNonBypassPrivateDnsWorking(true);
- wnm.forceReevaluation(Process.myUid());
- verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).atLeastOnce())
- .notifyNetworkTested(
- eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_PRIVDNS), eq(null));
- }
-
- @Test
- public void testDataStall_StallSuspectedAndSendMetrics() throws IOException {
- WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor();
- wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
- makeDnsTimeoutEvent(wrappedMonitor, 5);
- assertTrue(wrappedMonitor.isDataStall());
- verify(mDataStallStatsUtils, times(1)).write(makeEmptyDataStallDetectionStats(), any());
- }
-
- @Test
- public void testDataStall_NoStallSuspectedAndSendMetrics() throws IOException {
- WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor();
- wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000);
- makeDnsTimeoutEvent(wrappedMonitor, 3);
- assertFalse(wrappedMonitor.isDataStall());
- verify(mDataStallStatsUtils, never()).write(makeEmptyDataStallDetectionStats(), any());
- }
-
- @Test
- public void testCollectDataStallMetrics() {
- WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor();
-
- when(mTelephony.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE);
- when(mTelephony.getNetworkOperator()).thenReturn(TEST_MCCMNC);
- when(mTelephony.getSimOperator()).thenReturn(TEST_MCCMNC);
-
- DataStallDetectionStats.Builder stats =
- new DataStallDetectionStats.Builder()
- .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS)
- .setNetworkType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .setCellData(TelephonyManager.NETWORK_TYPE_LTE /* radioType */,
- true /* roaming */,
- TEST_MCCMNC /* networkMccmnc */,
- TEST_MCCMNC /* simMccmnc */,
- CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN /* signalStrength */);
- generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
-
- assertEquals(wrappedMonitor.buildDataStallDetectionStats(
- NetworkCapabilities.TRANSPORT_CELLULAR), stats.build());
-
- when(mWifi.getConnectionInfo()).thenReturn(mWifiInfo);
-
- stats = new DataStallDetectionStats.Builder()
- .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS)
- .setNetworkType(NetworkCapabilities.TRANSPORT_WIFI)
- .setWiFiData(mWifiInfo);
- generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD);
-
- assertEquals(
- wrappedMonitor.buildDataStallDetectionStats(NetworkCapabilities.TRANSPORT_WIFI),
- stats.build());
- }
-
- @Test
- public void testIgnoreHttpsProbe() throws Exception {
- setSslException(mHttpsConnection);
- setStatus(mHttpConnection, 204);
- // Expect to send HTTP, HTTPS, FALLBACK probe and evaluation result notifications to CS.
- final NetworkMonitor nm = runNetworkTest(VALIDATION_RESULT_PARTIAL);
-
- reset(mCallbacks);
- nm.setAcceptPartialConnectivity();
- // Expect to update evaluation result notifications to CS.
- verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTested(
- eq(VALIDATION_RESULT_PARTIAL | NETWORK_VALIDATION_RESULT_VALID), eq(null));
- }
-
- @Test
- public void testIsPartialConnectivity() throws IOException {
- setStatus(mHttpsConnection, 500);
- setStatus(mHttpConnection, 204);
- setStatus(mFallbackConnection, 500);
- runPartialConnectivityNetworkTest(VALIDATION_RESULT_PARTIAL);
-
- reset(mCallbacks);
- setStatus(mHttpsConnection, 500);
- setStatus(mHttpConnection, 500);
- setStatus(mFallbackConnection, 204);
- runPartialConnectivityNetworkTest(VALIDATION_RESULT_FALLBACK_PARTIAL);
- }
-
- private void assertIpAddressArrayEquals(String[] expected, InetAddress[] actual) {
- String[] actualStrings = new String[actual.length];
- for (int i = 0; i < actual.length; i++) {
- actualStrings[i] = actual[i].getHostAddress();
- }
- assertArrayEquals("Array of IP addresses differs", expected, actualStrings);
- }
-
- @Test
- public void testSendDnsProbeWithTimeout() throws Exception {
- WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor();
- final int shortTimeoutMs = 200;
-
- // Clear the wildcard DNS response created in setUp.
- mFakeDns.setAnswer("*", null);
-
- String[] expected = new String[]{"2001:db8::"};
- mFakeDns.setAnswer("www.google.com", expected);
- InetAddress[] actual = wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs);
- assertIpAddressArrayEquals(expected, actual);
-
- expected = new String[]{"2001:db8::", "192.0.2.1"};
- mFakeDns.setAnswer("www.googleapis.com", expected);
- actual = wnm.sendDnsProbeWithTimeout("www.googleapis.com", shortTimeoutMs);
- assertIpAddressArrayEquals(expected, actual);
-
- mFakeDns.setAnswer("www.google.com", new String[0]);
- try {
- wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs);
- fail("No DNS results, expected UnknownHostException");
- } catch (UnknownHostException e) {
- }
-
- mFakeDns.setAnswer("www.google.com", null);
- try {
- wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs);
- fail("DNS query timed out, expected UnknownHostException");
- } catch (UnknownHostException e) {
- }
- }
-
- @Test
- public void testNotifyNetwork_WithforceReevaluation() throws Exception {
- final NetworkMonitor nm = runValidatedNetworkTest();
- // Verify forceReevalution will not reset the validation result but only probe result until
- // getting the validation result.
- reset(mCallbacks);
- setSslException(mHttpsConnection);
- setStatus(mHttpConnection, 500);
- setStatus(mFallbackConnection, 204);
- nm.forceReevaluation(Process.myUid());
- final ArgumentCaptor<Integer> intCaptor = ArgumentCaptor.forClass(Integer.class);
- // Expect to send HTTP, HTTPs, FALLBACK and evaluation results.
- verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(4))
- .notifyNetworkTested(intCaptor.capture(), any());
- List<Integer> intArgs = intCaptor.getAllValues();
-
- // None of these exact values can be known in advance except for intArgs.get(0) because the
- // HTTP and HTTPS probes race and the order in which they complete is non-deterministic.
- // Thus, check only exact value for intArgs.get(0) and only check the validation result for
- // the rest ones.
- assertEquals(Integer.valueOf(NETWORK_VALIDATION_PROBE_DNS
- | NETWORK_VALIDATION_PROBE_FALLBACK | NETWORK_VALIDATION_RESULT_VALID),
- intArgs.get(0));
- assertTrue((intArgs.get(1) & NETWORK_VALIDATION_RESULT_VALID) != 0);
- assertTrue((intArgs.get(2) & NETWORK_VALIDATION_RESULT_VALID) != 0);
- assertTrue((intArgs.get(3) & NETWORK_VALIDATION_RESULT_PARTIAL) != 0);
- assertTrue((intArgs.get(3) & NETWORK_VALIDATION_RESULT_VALID) == 0);
- }
-
- @Test
- public void testEvaluationState_clearProbeResults() throws Exception {
- final NetworkMonitor nm = runValidatedNetworkTest();
- nm.getEvaluationState().clearProbeResults();
- // Verify probe results are all reset and only evaluation result left.
- assertEquals(NETWORK_VALIDATION_RESULT_VALID,
- nm.getEvaluationState().getNetworkTestResult());
- }
-
- @Test
- public void testEvaluationState_reportProbeResult() throws Exception {
- final NetworkMonitor nm = runValidatedNetworkTest();
-
- reset(mCallbacks);
-
- nm.reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, CaptivePortalProbeResult.SUCCESS);
- // Verify result should be appended and notifyNetworkTested callback is triggered once.
- assertEquals(nm.getEvaluationState().getNetworkTestResult(),
- VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_HTTP);
- verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTested(
- eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_PROBE_HTTP), any());
-
- nm.reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTP, CaptivePortalProbeResult.FAILED);
- // Verify DNS probe result should not be cleared.
- assertTrue((nm.getEvaluationState().getNetworkTestResult() & NETWORK_VALIDATION_PROBE_DNS)
- == NETWORK_VALIDATION_PROBE_DNS);
- }
-
- @Test
- public void testEvaluationState_reportEvaluationResult() throws Exception {
- final NetworkMonitor nm = runValidatedNetworkTest();
-
- nm.getEvaluationState().reportEvaluationResult(NETWORK_VALIDATION_RESULT_PARTIAL,
- null /* redirectUrl */);
- verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTested(
- eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS
- | NETWORK_VALIDATION_RESULT_PARTIAL), eq(null));
-
- nm.getEvaluationState().reportEvaluationResult(
- NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_PARTIAL,
- null /* redirectUrl */);
- verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTested(
- eq(VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_PARTIAL), eq(null));
-
- nm.getEvaluationState().reportEvaluationResult(VALIDATION_RESULT_INVALID,
- TEST_REDIRECT_URL);
- verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)).notifyNetworkTested(
- eq(NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS),
- eq(TEST_REDIRECT_URL));
- }
-
- private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
- for (int i = 0; i < count; i++) {
- wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
- RETURN_CODE_DNS_TIMEOUT);
- }
- }
-
- private void makeDnsSuccessEvent(WrappedNetworkMonitor wrappedMonitor, int count) {
- for (int i = 0; i < count; i++) {
- wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount(
- RETURN_CODE_DNS_SUCCESS);
- }
- }
-
- private DataStallDetectionStats makeEmptyDataStallDetectionStats() {
- return new DataStallDetectionStats.Builder().build();
- }
-
- private void setDataStallEvaluationType(int type) {
- when(mDependencies.getDeviceConfigPropertyInt(any(),
- eq(CONFIG_DATA_STALL_EVALUATION_TYPE), anyInt())).thenReturn(type);
- }
-
- private void setMinDataStallEvaluateInterval(int time) {
- when(mDependencies.getDeviceConfigPropertyInt(any(),
- eq(CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL), anyInt())).thenReturn(time);
- }
-
- private void setValidDataStallDnsTimeThreshold(int time) {
- when(mDependencies.getDeviceConfigPropertyInt(any(),
- eq(CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD), anyInt())).thenReturn(time);
- }
-
- private void setConsecutiveDnsTimeoutThreshold(int num) {
- when(mDependencies.getDeviceConfigPropertyInt(any(),
- eq(CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD), anyInt())).thenReturn(num);
- }
-
- private void setFallbackUrl(String url) {
- when(mDependencies.getSetting(any(),
- eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL), any())).thenReturn(url);
- }
-
- private void setOtherFallbackUrls(String urls) {
- when(mDependencies.getDeviceConfigProperty(any(),
- eq(CAPTIVE_PORTAL_OTHER_FALLBACK_URLS), any())).thenReturn(urls);
- }
-
- private void setFallbackSpecs(String specs) {
- when(mDependencies.getDeviceConfigProperty(any(),
- eq(CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS), any())).thenReturn(specs);
- }
-
- private void setCaptivePortalMode(int mode) {
- when(mDependencies.getSetting(any(),
- eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())).thenReturn(mode);
- }
-
- private void runPortalNetworkTest(int result) {
- // The network test event will be triggered twice with the same result. Expect to capture
- // the second one with direct url.
- runPortalNetworkTest(result,
- (VerificationWithTimeout) timeout(HANDLER_TIMEOUT_MS).times(2));
- }
-
- private void runPortalNetworkTest(int result, VerificationWithTimeout mode) {
- runNetworkTest(result, mode);
- assertEquals(1, mRegisteredReceivers.size());
- assertNotNull(mNetworkTestedRedirectUrlCaptor.getValue());
- }
-
- private void runNotPortalNetworkTest() {
- runNetworkTest(VALIDATION_RESULT_VALID);
- assertEquals(0, mRegisteredReceivers.size());
- assertNull(mNetworkTestedRedirectUrlCaptor.getValue());
- }
-
- private void runNoValidationNetworkTest() {
- runNetworkTest(NETWORK_VALIDATION_RESULT_VALID);
- assertEquals(0, mRegisteredReceivers.size());
- assertNull(mNetworkTestedRedirectUrlCaptor.getValue());
- }
-
- private void runFailedNetworkTest() {
- runNetworkTest(VALIDATION_RESULT_INVALID);
- assertEquals(0, mRegisteredReceivers.size());
- assertNull(mNetworkTestedRedirectUrlCaptor.getValue());
- }
-
- private void runPartialConnectivityNetworkTest(int result) {
- runNetworkTest(result);
- assertEquals(0, mRegisteredReceivers.size());
- assertNull(mNetworkTestedRedirectUrlCaptor.getValue());
- }
-
- private NetworkMonitor runValidatedNetworkTest() throws Exception {
- setStatus(mHttpsConnection, 204);
- setStatus(mHttpConnection, 204);
- // Expect to send HTTPs and evaluation results.
- return runNetworkTest(VALIDATION_RESULT_VALID);
- }
-
- private NetworkMonitor runNetworkTest(int testResult) {
- return runNetworkTest(METERED_CAPABILITIES, testResult, getGeneralVerification());
- }
-
- private NetworkMonitor runNetworkTest(int testResult, VerificationWithTimeout mode) {
- return runNetworkTest(METERED_CAPABILITIES, testResult, mode);
- }
-
- private NetworkMonitor runNetworkTest(NetworkCapabilities nc, int testResult,
- VerificationWithTimeout mode) {
- final NetworkMonitor monitor = makeMonitor(nc);
- monitor.notifyNetworkConnected(TEST_LINK_PROPERTIES, nc);
- try {
- verify(mCallbacks, mode)
- .notifyNetworkTested(eq(testResult), mNetworkTestedRedirectUrlCaptor.capture());
- } catch (RemoteException e) {
- fail("Unexpected exception: " + e);
- }
- waitForIdle(monitor.getHandler());
-
- return monitor;
- }
-
- private void setSslException(HttpURLConnection connection) throws IOException {
- doThrow(new SSLHandshakeException("Invalid cert")).when(connection).getResponseCode();
- }
-
- private void set302(HttpURLConnection connection, String location) throws IOException {
- setStatus(connection, 302);
- doReturn(location).when(connection).getHeaderField(LOCATION_HEADER);
- }
-
- private void setPortal302(HttpURLConnection connection) throws IOException {
- set302(connection, "http://login.example.com");
- }
-
- private void setStatus(HttpURLConnection connection, int status) throws IOException {
- doReturn(status).when(connection).getResponseCode();
- }
-
- private void generateTimeoutDnsEvent(DataStallDetectionStats.Builder stats, int num) {
- for (int i = 0; i < num; i++) {
- stats.addDnsEvent(RETURN_CODE_DNS_TIMEOUT, 123456789 /* timeMs */);
- }
- }
-
- private VerificationWithTimeout getGeneralVerification() {
- return (VerificationWithTimeout) timeout(HANDLER_TIMEOUT_MS).atLeastOnce();
- }
-
-}
-
diff --git a/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java b/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
deleted file mode 100644
index 64fe3a6..0000000
--- a/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java
+++ /dev/null
@@ -1,756 +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.server.connectivity.ipmemorystore;
-
-import static com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService.InterruptMaintenance;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doReturn;
-
-import android.app.job.JobScheduler;
-import android.content.Context;
-import android.net.ipmemorystore.Blob;
-import android.net.ipmemorystore.IOnBlobRetrievedListener;
-import android.net.ipmemorystore.IOnL2KeyResponseListener;
-import android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener;
-import android.net.ipmemorystore.IOnSameL3NetworkResponseListener;
-import android.net.ipmemorystore.IOnStatusListener;
-import android.net.ipmemorystore.NetworkAttributes;
-import android.net.ipmemorystore.NetworkAttributesParcelable;
-import android.net.ipmemorystore.SameL3NetworkResponse;
-import android.net.ipmemorystore.SameL3NetworkResponseParcelable;
-import android.net.ipmemorystore.Status;
-import android.net.ipmemorystore.StatusParcelable;
-import android.os.ConditionVariable;
-import android.os.IBinder;
-import android.os.RemoteException;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.lang.reflect.Modifier;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
-
-/** Unit tests for {@link IpMemoryStoreService}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class IpMemoryStoreServiceTest {
- private static final String TEST_CLIENT_ID = "testClientId";
- private static final String TEST_DATA_NAME = "testData";
-
- private static final int TEST_DATABASE_SIZE_THRESHOLD = 100 * 1024; //100KB
- private static final int DEFAULT_TIMEOUT_MS = 5000;
- private static final int LONG_TIMEOUT_MS = 30000;
- private static final int FAKE_KEY_COUNT = 20;
- private static final long LEASE_EXPIRY_NULL = -1L;
- private static final int MTU_NULL = -1;
- private static final String[] FAKE_KEYS;
- private static final byte[] TEST_BLOB_DATA = new byte[] { -3, 6, 8, -9, 12,
- -128, 0, 89, 112, 91, -34 };
- static {
- FAKE_KEYS = new String[FAKE_KEY_COUNT];
- for (int i = 0; i < FAKE_KEYS.length; ++i) {
- FAKE_KEYS[i] = "fakeKey" + i;
- }
- }
-
- @Mock
- private Context mMockContext;
- @Mock
- private JobScheduler mMockJobScheduler;
- private File mDbFile;
-
- private IpMemoryStoreService mService;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- final Context context = InstrumentationRegistry.getContext();
- final File dir = context.getFilesDir();
- mDbFile = new File(dir, "test.db");
- doReturn(mDbFile).when(mMockContext).getDatabasePath(anyString());
- doReturn(mMockJobScheduler).when(mMockContext)
- .getSystemService(Context.JOB_SCHEDULER_SERVICE);
- mService = new IpMemoryStoreService(mMockContext) {
- @Override
- protected int getDbSizeThreshold() {
- return TEST_DATABASE_SIZE_THRESHOLD;
- }
-
- @Override
- boolean isDbSizeOverThreshold() {
- // Add a 100ms delay here for pausing maintenance job a while. Interrupted flag can
- // be set at this time.
- waitForMs(100);
- return super.isDbSizeOverThreshold();
- }
- };
- }
-
- @After
- public void tearDown() {
- mService.shutdown();
- mDbFile.delete();
- }
-
- /** Helper method to build test network attributes */
- private static NetworkAttributes.Builder buildTestNetworkAttributes(
- final Inet4Address ipAddress, final long expiry, final String hint,
- final List<InetAddress> dnsServers, final int mtu) {
- final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
- if (null != ipAddress) {
- na.setAssignedV4Address(ipAddress);
- }
- if (LEASE_EXPIRY_NULL != expiry) {
- na.setAssignedV4AddressExpiry(expiry);
- }
- if (null != hint) {
- na.setGroupHint(hint);
- }
- if (null != dnsServers) {
- na.setDnsAddresses(dnsServers);
- }
- if (MTU_NULL != mtu) {
- na.setMtu(mtu);
- }
- return na;
- }
-
- /** Helper method to make a vanilla IOnStatusListener */
- private IOnStatusListener onStatus(Consumer<Status> functor) {
- return new IOnStatusListener() {
- @Override
- public void onComplete(final StatusParcelable statusParcelable) throws RemoteException {
- functor.accept(new Status(statusParcelable));
- }
-
- @Override
- public IBinder asBinder() {
- return null;
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
- };
- }
-
- /** Helper method to make an IOnBlobRetrievedListener */
- private interface OnBlobRetrievedListener {
- void onBlobRetrieved(Status status, String l2Key, String name, byte[] data);
- }
- private IOnBlobRetrievedListener onBlobRetrieved(final OnBlobRetrievedListener functor) {
- return new IOnBlobRetrievedListener() {
- @Override
- public void onBlobRetrieved(final StatusParcelable statusParcelable,
- final String l2Key, final String name, final Blob blob) throws RemoteException {
- functor.onBlobRetrieved(new Status(statusParcelable), l2Key, name,
- null == blob ? null : blob.data);
- }
-
- @Override
- public IBinder asBinder() {
- return null;
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
- };
- }
-
- /** Helper method to make an IOnNetworkAttributesRetrievedListener */
- private interface OnNetworkAttributesRetrievedListener {
- void onNetworkAttributesRetrieved(Status status, String l2Key, NetworkAttributes attr);
- }
- private IOnNetworkAttributesRetrievedListener onNetworkAttributesRetrieved(
- final OnNetworkAttributesRetrievedListener functor) {
- return new IOnNetworkAttributesRetrievedListener() {
- @Override
- public void onNetworkAttributesRetrieved(final StatusParcelable status,
- final String l2Key, final NetworkAttributesParcelable attributes)
- throws RemoteException {
- functor.onNetworkAttributesRetrieved(new Status(status), l2Key,
- null == attributes ? null : new NetworkAttributes(attributes));
- }
-
- @Override
- public IBinder asBinder() {
- return null;
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
- };
- }
-
- /** Helper method to make an IOnSameNetworkResponseListener */
- private interface OnSameL3NetworkResponseListener {
- void onSameL3NetworkResponse(Status status, SameL3NetworkResponse answer);
- }
- private IOnSameL3NetworkResponseListener onSameResponse(
- final OnSameL3NetworkResponseListener functor) {
- return new IOnSameL3NetworkResponseListener() {
- @Override
- public void onSameL3NetworkResponse(final StatusParcelable status,
- final SameL3NetworkResponseParcelable sameL3Network)
- throws RemoteException {
- functor.onSameL3NetworkResponse(new Status(status),
- null == sameL3Network ? null : new SameL3NetworkResponse(sameL3Network));
- }
-
- @Override
- public IBinder asBinder() {
- return null;
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
- };
- }
-
- /** Helper method to make an IOnL2KeyResponseListener */
- private interface OnL2KeyResponseListener {
- void onL2KeyResponse(Status status, String key);
- }
- private IOnL2KeyResponseListener onL2KeyResponse(final OnL2KeyResponseListener functor) {
- return new IOnL2KeyResponseListener() {
- @Override
- public void onL2KeyResponse(final StatusParcelable status, final String key)
- throws RemoteException {
- functor.onL2KeyResponse(new Status(status), key);
- }
-
- @Override
- public IBinder asBinder() {
- return null;
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
- };
- }
-
- // Helper method to factorize some boilerplate
- private void doLatched(final String timeoutMessage, final Consumer<CountDownLatch> functor) {
- doLatched(timeoutMessage, functor, DEFAULT_TIMEOUT_MS);
- }
-
- private void doLatched(final String timeoutMessage, final Consumer<CountDownLatch> functor,
- final int timeout) {
- final CountDownLatch latch = new CountDownLatch(1);
- functor.accept(latch);
- try {
- if (!latch.await(timeout, TimeUnit.MILLISECONDS)) {
- fail(timeoutMessage);
- }
- } catch (InterruptedException e) {
- fail("Thread was interrupted");
- }
- }
-
- // Helper method to store network attributes to database
- private void storeAttributes(final String l2Key, final NetworkAttributes na) {
- storeAttributes("Did not complete storing attributes", l2Key, na);
- }
- private void storeAttributes(final String timeoutMessage, final String l2Key,
- final NetworkAttributes na) {
- doLatched(timeoutMessage, latch -> mService.storeNetworkAttributes(l2Key, na.toParcelable(),
- onStatus(status -> {
- assertTrue("Store not successful : " + status.resultCode, status.isSuccess());
- latch.countDown();
- })));
- }
-
- // Helper method to store blob data to database
- private void storeBlobOrFail(final String l2Key, final Blob b, final byte[] data) {
- storeBlobOrFail("Did not complete storing private data", l2Key, b, data);
- }
- private void storeBlobOrFail(final String timeoutMessage, final String l2Key, final Blob b,
- final byte[] data) {
- b.data = data;
- doLatched(timeoutMessage, latch -> mService.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME,
- b, onStatus(status -> {
- assertTrue("Store status not successful : " + status.resultCode,
- status.isSuccess());
- latch.countDown();
- })));
- }
-
- /** Insert large data that db size will be over threshold for maintenance test usage. */
- private void insertFakeDataAndOverThreshold() {
- try {
- final NetworkAttributes.Builder na = buildTestNetworkAttributes(
- (Inet4Address) Inet4Address.getByName("1.2.3.4"), LEASE_EXPIRY_NULL,
- "hint1", Arrays.asList(Inet6Address.getByName("0A1C:2E40:480A::1CA6")),
- 219);
- final long time = System.currentTimeMillis() - 1;
- for (int i = 0; i < 1000; i++) {
- int errorCode = IpMemoryStoreDatabase.storeNetworkAttributes(
- mService.mDb,
- "fakeKey" + i,
- // Let first 100 records get expiry.
- i < 100 ? time : time + TimeUnit.HOURS.toMillis(i),
- na.build());
- assertEquals(errorCode, Status.SUCCESS);
-
- errorCode = IpMemoryStoreDatabase.storeBlob(
- mService.mDb, "fakeKey" + i, TEST_CLIENT_ID, TEST_DATA_NAME,
- TEST_BLOB_DATA);
- assertEquals(errorCode, Status.SUCCESS);
- }
-
- // After added 5000 records, db size is larger than fake threshold(100KB).
- assertTrue(mService.isDbSizeOverThreshold());
- } catch (final UnknownHostException e) {
- fail("Insert fake data fail");
- }
- }
-
- /** Wait for assigned time. */
- private void waitForMs(long ms) {
- try {
- Thread.sleep(ms);
- } catch (final InterruptedException e) {
- fail("Thread was interrupted");
- }
- }
-
- @Test
- public void testNetworkAttributes() throws UnknownHostException {
- final String l2Key = FAKE_KEYS[0];
- final NetworkAttributes.Builder na = buildTestNetworkAttributes(
- (Inet4Address) Inet4Address.getByName("1.2.3.4"),
- System.currentTimeMillis() + 7_200_000, "hint1", null, 219);
- NetworkAttributes attributes = na.build();
- storeAttributes(l2Key, attributes);
-
- doLatched("Did not complete retrieving attributes", latch ->
- mService.retrieveNetworkAttributes(l2Key, onNetworkAttributesRetrieved(
- (status, key, attr) -> {
- assertTrue("Retrieve network attributes not successful : "
- + status.resultCode, status.isSuccess());
- assertEquals(l2Key, key);
- assertEquals(attributes, attr);
- latch.countDown();
- })));
-
- final NetworkAttributes.Builder na2 = new NetworkAttributes.Builder();
- na.setDnsAddresses(Arrays.asList(
- new InetAddress[] {Inet6Address.getByName("0A1C:2E40:480A::1CA6")}));
- final NetworkAttributes attributes2 = na2.build();
- storeAttributes("Did not complete storing attributes 2", l2Key, attributes2);
-
- doLatched("Did not complete retrieving attributes 2", latch ->
- mService.retrieveNetworkAttributes(l2Key, onNetworkAttributesRetrieved(
- (status, key, attr) -> {
- assertTrue("Retrieve network attributes not successful : "
- + status.resultCode, status.isSuccess());
- assertEquals(l2Key, key);
- assertEquals(attributes.assignedV4Address, attr.assignedV4Address);
- assertEquals(attributes.assignedV4AddressExpiry,
- attr.assignedV4AddressExpiry);
- assertEquals(attributes.groupHint, attr.groupHint);
- assertEquals(attributes.mtu, attr.mtu);
- assertEquals(attributes2.dnsAddresses, attr.dnsAddresses);
- latch.countDown();
- })));
-
- doLatched("Did not complete retrieving attributes 3", latch ->
- mService.retrieveNetworkAttributes(l2Key + "nonexistent",
- onNetworkAttributesRetrieved(
- (status, key, attr) -> {
- assertTrue("Retrieve network attributes not successful : "
- + status.resultCode, status.isSuccess());
- assertEquals(l2Key + "nonexistent", key);
- assertNull("Retrieved data not stored", attr);
- latch.countDown();
- }
- )));
-
- // Verify that this test does not miss any new field added later.
- // If any field is added to NetworkAttributes it must be tested here for storing
- // and retrieving.
- assertEquals(5, Arrays.stream(NetworkAttributes.class.getDeclaredFields())
- .filter(f -> !Modifier.isStatic(f.getModifiers())).count());
- }
-
- @Test
- public void testInvalidAttributes() {
- doLatched("Did not complete storing bad attributes", latch ->
- mService.storeNetworkAttributes("key", null, onStatus(status -> {
- assertFalse("Success storing on a null key",
- status.isSuccess());
- assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode);
- latch.countDown();
- })));
-
- final NetworkAttributes na = new NetworkAttributes.Builder().setMtu(2).build();
- doLatched("Did not complete storing bad attributes", latch ->
- mService.storeNetworkAttributes(null, na.toParcelable(), onStatus(status -> {
- assertFalse("Success storing null attributes on a null key",
- status.isSuccess());
- assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode);
- latch.countDown();
- })));
-
- doLatched("Did not complete storing bad attributes", latch ->
- mService.storeNetworkAttributes(null, null, onStatus(status -> {
- assertFalse("Success storing null attributes on a null key",
- status.isSuccess());
- assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode);
- latch.countDown();
- })));
-
- doLatched("Did not complete retrieving bad attributes", latch ->
- mService.retrieveNetworkAttributes(null, onNetworkAttributesRetrieved(
- (status, key, attr) -> {
- assertFalse("Success retrieving attributes for a null key",
- status.isSuccess());
- assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode);
- assertNull(key);
- assertNull(attr);
- latch.countDown();
- })));
- }
-
- @Test
- public void testPrivateData() {
- final String l2Key = FAKE_KEYS[0];
- final Blob b = new Blob();
- storeBlobOrFail(l2Key, b, TEST_BLOB_DATA);
-
- doLatched("Did not complete retrieving private data", latch ->
- mService.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, onBlobRetrieved(
- (status, key, name, data) -> {
- assertTrue("Retrieve blob status not successful : " + status.resultCode,
- status.isSuccess());
- assertEquals(l2Key, key);
- assertEquals(name, TEST_DATA_NAME);
- assertTrue(Arrays.equals(b.data, data));
- latch.countDown();
- })));
-
- // Most puzzling error message ever
- doLatched("Did not complete retrieving nothing", latch ->
- mService.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME + "2", onBlobRetrieved(
- (status, key, name, data) -> {
- assertTrue("Retrieve blob status not successful : " + status.resultCode,
- status.isSuccess());
- assertEquals(l2Key, key);
- assertEquals(name, TEST_DATA_NAME + "2");
- assertNull(data);
- latch.countDown();
- })));
- }
-
- @Test
- public void testFindL2Key() throws UnknownHostException {
- final NetworkAttributes.Builder na = new NetworkAttributes.Builder();
- na.setGroupHint("hint0");
- storeAttributes(FAKE_KEYS[0], na.build());
-
- na.setDnsAddresses(Arrays.asList(
- new InetAddress[] {Inet6Address.getByName("8D56:9AF1::08EE:20F1")}));
- na.setMtu(219);
- storeAttributes(FAKE_KEYS[1], na.build());
- na.setMtu(null);
- na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
- na.setDnsAddresses(Arrays.asList(
- new InetAddress[] {Inet6Address.getByName("0A1C:2E40:480A::1CA6")}));
- na.setGroupHint("hint1");
- storeAttributes(FAKE_KEYS[2], na.build());
- na.setMtu(219);
- storeAttributes(FAKE_KEYS[3], na.build());
- na.setMtu(240);
- storeAttributes(FAKE_KEYS[4], na.build());
- na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("5.6.7.8"));
- storeAttributes(FAKE_KEYS[5], na.build());
-
- // Matches key 5 exactly
- doLatched("Did not finish finding L2Key", latch ->
- mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
- assertTrue("Retrieve network sameness not successful : " + status.resultCode,
- status.isSuccess());
- assertEquals(FAKE_KEYS[5], key);
- latch.countDown();
- })));
-
- // MTU matches key 4 but v4 address matches key 5. The latter is stronger.
- na.setMtu(240);
- doLatched("Did not finish finding L2Key", latch ->
- mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
- assertTrue("Retrieve network sameness not successful : " + status.resultCode,
- status.isSuccess());
- assertEquals(FAKE_KEYS[5], key);
- latch.countDown();
- })));
-
- // Closest to key 3 (indeed, identical)
- na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
- na.setMtu(219);
- doLatched("Did not finish finding L2Key", latch ->
- mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
- assertTrue("Retrieve network sameness not successful : " + status.resultCode,
- status.isSuccess());
- assertEquals(FAKE_KEYS[3], key);
- latch.countDown();
- })));
-
- // Group hint alone must not be strong enough to override the rest
- na.setGroupHint("hint0");
- doLatched("Did not finish finding L2Key", latch ->
- mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
- assertTrue("Retrieve network sameness not successful : " + status.resultCode,
- status.isSuccess());
- assertEquals(FAKE_KEYS[3], key);
- latch.countDown();
- })));
-
- // Still closest to key 3, though confidence is lower
- na.setGroupHint("hint1");
- na.setDnsAddresses(null);
- doLatched("Did not finish finding L2Key", latch ->
- mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
- assertTrue("Retrieve network sameness not successful : " + status.resultCode,
- status.isSuccess());
- assertEquals(FAKE_KEYS[3], key);
- latch.countDown();
- })));
-
- // But changing the MTU makes this closer to key 4
- na.setMtu(240);
- doLatched("Did not finish finding L2Key", latch ->
- mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
- assertTrue("Retrieve network sameness not successful : " + status.resultCode,
- status.isSuccess());
- assertEquals(FAKE_KEYS[4], key);
- latch.countDown();
- })));
-
- // MTU alone not strong enough to make this group-close
- na.setGroupHint(null);
- na.setDnsAddresses(null);
- na.setAssignedV4Address(null);
- doLatched("Did not finish finding L2Key", latch ->
- mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> {
- assertTrue("Retrieve network sameness not successful : " + status.resultCode,
- status.isSuccess());
- assertNull(key);
- latch.countDown();
- })));
- }
-
- private void assertNetworksSameness(final String key1, final String key2, final int sameness) {
- doLatched("Did not finish evaluating sameness", latch ->
- mService.isSameNetwork(key1, key2, onSameResponse((status, answer) -> {
- assertTrue("Retrieve network sameness not successful : " + status.resultCode,
- status.isSuccess());
- assertEquals(sameness, answer.getNetworkSameness());
- latch.countDown();
- })));
- }
-
- @Test
- public void testIsSameNetwork() throws UnknownHostException {
- final NetworkAttributes.Builder na = buildTestNetworkAttributes(
- (Inet4Address) Inet4Address.getByName("1.2.3.4"), LEASE_EXPIRY_NULL,
- "hint1", Arrays.asList(Inet6Address.getByName("0A1C:2E40:480A::1CA6")),
- 219);
-
- storeAttributes(FAKE_KEYS[0], na.build());
- // 0 and 1 have identical attributes
- storeAttributes(FAKE_KEYS[1], na.build());
-
- // Hopefully only the MTU being different still means it's the same network
- na.setMtu(200);
- storeAttributes(FAKE_KEYS[2], na.build());
-
- // Hopefully different MTU, assigned V4 address and grouphint make a different network,
- // even with identical DNS addresses
- na.setAssignedV4Address(null);
- na.setGroupHint("hint2");
- storeAttributes(FAKE_KEYS[3], na.build());
-
- assertNetworksSameness(FAKE_KEYS[0], FAKE_KEYS[1], SameL3NetworkResponse.NETWORK_SAME);
- assertNetworksSameness(FAKE_KEYS[0], FAKE_KEYS[2], SameL3NetworkResponse.NETWORK_SAME);
- assertNetworksSameness(FAKE_KEYS[1], FAKE_KEYS[2], SameL3NetworkResponse.NETWORK_SAME);
- assertNetworksSameness(FAKE_KEYS[0], FAKE_KEYS[3], SameL3NetworkResponse.NETWORK_DIFFERENT);
- assertNetworksSameness(FAKE_KEYS[0], "neverInsertedKey",
- SameL3NetworkResponse.NETWORK_NEVER_CONNECTED);
-
- doLatched("Did not finish evaluating sameness", latch ->
- mService.isSameNetwork(null, null, onSameResponse((status, answer) -> {
- assertFalse("Retrieve network sameness suspiciously successful : "
- + status.resultCode, status.isSuccess());
- assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode);
- assertNull(answer);
- latch.countDown();
- })));
- }
-
- @Test
- public void testFullMaintenance() {
- insertFakeDataAndOverThreshold();
-
- final InterruptMaintenance im = new InterruptMaintenance(0/* Fake JobId */);
- // Do full maintenance and then db size should go down and meet the threshold.
- doLatched("Maintenance unexpectedly completed successfully", latch ->
- mService.fullMaintenance(onStatus((status) -> {
- assertTrue("Execute full maintenance failed: "
- + status.resultCode, status.isSuccess());
- latch.countDown();
- }), im), LONG_TIMEOUT_MS);
-
- // Assume that maintenance is successful, db size shall meet the threshold.
- assertFalse(mService.isDbSizeOverThreshold());
- }
-
- @Test
- public void testInterruptMaintenance() {
- insertFakeDataAndOverThreshold();
-
- final InterruptMaintenance im = new InterruptMaintenance(0/* Fake JobId */);
-
- // Test interruption immediately.
- im.setInterrupted(true);
- // Do full maintenance and the expectation is not completed by interruption.
- doLatched("Maintenance unexpectedly completed successfully", latch ->
- mService.fullMaintenance(onStatus((status) -> {
- assertFalse(status.isSuccess());
- latch.countDown();
- }), im), LONG_TIMEOUT_MS);
-
- // Assume that no data are removed, db size shall be over the threshold.
- assertTrue(mService.isDbSizeOverThreshold());
-
- // Reset the flag and test interruption during maintenance.
- im.setInterrupted(false);
-
- final ConditionVariable latch = new ConditionVariable();
- // Do full maintenance and the expectation is not completed by interruption.
- mService.fullMaintenance(onStatus((status) -> {
- assertFalse(status.isSuccess());
- latch.open();
- }), im);
-
- // Give a little bit of time for maintenance to start up for realism
- waitForMs(50);
- // Interrupt maintenance job.
- im.setInterrupted(true);
-
- if (!latch.block(LONG_TIMEOUT_MS)) {
- fail("Maintenance unexpectedly completed successfully");
- }
-
- // Assume that only do dropAllExpiredRecords method in previous maintenance, db size shall
- // still be over the threshold.
- assertTrue(mService.isDbSizeOverThreshold());
- }
-
- @Test
- public void testFactoryReset() throws UnknownHostException {
- final String l2Key = FAKE_KEYS[0];
-
- // store network attributes
- final NetworkAttributes.Builder na = buildTestNetworkAttributes(
- (Inet4Address) Inet4Address.getByName("1.2.3.4"),
- System.currentTimeMillis() + 7_200_000, "hint1", null, 219);
- storeAttributes(l2Key, na.build());
-
- // store private data blob
- final Blob b = new Blob();
- storeBlobOrFail(l2Key, b, TEST_BLOB_DATA);
-
- // wipe all data in Database
- mService.factoryReset();
-
- // retrieved network attributes should be null
- doLatched("Did not complete retrieving attributes", latch ->
- mService.retrieveNetworkAttributes(l2Key, onNetworkAttributesRetrieved(
- (status, key, attr) -> {
- assertTrue("Retrieve network attributes not successful : "
- + status.resultCode, status.isSuccess());
- assertEquals(l2Key, key);
- assertNull(attr);
- latch.countDown();
- })));
-
- // retrieved private data blob should be null
- doLatched("Did not complete retrieving private data", latch ->
- mService.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, onBlobRetrieved(
- (status, key, name, data) -> {
- assertTrue("Retrieve blob status not successful : " + status.resultCode,
- status.isSuccess());
- assertEquals(l2Key, key);
- assertEquals(name, TEST_DATA_NAME);
- assertNull(data);
- latch.countDown();
- })));
- }
-
- public void testTasksAreSerial() {
- final long sleepTimeMs = 1000;
- final long startTime = System.currentTimeMillis();
- mService.retrieveNetworkAttributes("somekey", onNetworkAttributesRetrieved(
- (status, key, attr) -> {
- assertTrue("Unexpected status : " + status.resultCode, status.isSuccess());
- try {
- Thread.sleep(sleepTimeMs);
- } catch (InterruptedException e) {
- fail("InterruptedException");
- }
- }));
- doLatched("Serial tasks timing out", latch ->
- mService.retrieveNetworkAttributes("somekey", onNetworkAttributesRetrieved(
- (status, key, attr) -> {
- assertTrue("Unexpected status : " + status.resultCode,
- status.isSuccess());
- assertTrue(System.currentTimeMillis() >= startTime + sleepTimeMs);
- })), DEFAULT_TIMEOUT_MS);
- }
-}
diff --git a/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/ipmemorystore/RelevanceUtilsTests.java b/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/ipmemorystore/RelevanceUtilsTests.java
deleted file mode 100644
index 3d3aabc..0000000
--- a/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/ipmemorystore/RelevanceUtilsTests.java
+++ /dev/null
@@ -1,149 +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.server.connectivity.ipmemorystore;
-
-import static com.android.server.connectivity.ipmemorystore.RelevanceUtils.CAPPED_RELEVANCE;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/** Unit tests for {@link RelevanceUtils}. */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class RelevanceUtilsTests {
- @Test
- public void testComputeRelevanceForTargetDate() {
- final long dayInMillis = 24L * 60 * 60 * 1000;
- final long base = 1_000_000L; // any given point in time
- // Relevance when the network expires in 1000 years must be capped
- assertEquals(CAPPED_RELEVANCE, RelevanceUtils.computeRelevanceForTargetDate(
- base + 1000L * dayInMillis, base));
- // Relevance when expiry is before the date must be 0
- assertEquals(0, RelevanceUtils.computeRelevanceForTargetDate(base - 1, base));
- // Make sure the relevance for a given target date is higher if the expiry is further
- // in the future
- assertTrue(RelevanceUtils.computeRelevanceForTargetDate(base + 100 * dayInMillis, base)
- < RelevanceUtils.computeRelevanceForTargetDate(base + 150 * dayInMillis, base));
-
- // Make sure the relevance falls slower as the expiry is closing in. This is to ensure
- // the decay is indeed logarithmic.
- final int relevanceAtExpiry = RelevanceUtils.computeRelevanceForTargetDate(base, base);
- final int relevance50DaysBeforeExpiry =
- RelevanceUtils.computeRelevanceForTargetDate(base + 50 * dayInMillis, base);
- final int relevance100DaysBeforeExpiry =
- RelevanceUtils.computeRelevanceForTargetDate(base + 100 * dayInMillis, base);
- final int relevance150DaysBeforeExpiry =
- RelevanceUtils.computeRelevanceForTargetDate(base + 150 * dayInMillis, base);
- assertEquals(0, relevanceAtExpiry);
- assertTrue(relevance50DaysBeforeExpiry - relevanceAtExpiry
- < relevance100DaysBeforeExpiry - relevance50DaysBeforeExpiry);
- assertTrue(relevance100DaysBeforeExpiry - relevance50DaysBeforeExpiry
- < relevance150DaysBeforeExpiry - relevance100DaysBeforeExpiry);
- }
-
- @Test
- public void testIncreaseRelevance() {
- long expiry = System.currentTimeMillis();
-
- final long firstBump = RelevanceUtils.bumpExpiryDate(expiry);
- // Though a few milliseconds might have elapsed, the first bump should push the duration
- // to days in the future, so unless this test takes literal days between these two lines,
- // this should always pass.
- assertTrue(firstBump > expiry);
-
- expiry = 0;
- long lastDifference = Long.MAX_VALUE;
- // The relevance should be capped in at most this many steps. Otherwise, fail.
- final int steps = 1000;
- for (int i = 0; i < steps; ++i) {
- final long newExpiry = RelevanceUtils.bumpExpiryDuration(expiry);
- if (newExpiry == expiry) {
- // The relevance should be capped. Make sure it is, then exit without failure.
- assertEquals(newExpiry, RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS);
- return;
- }
- // Make sure the new expiry is further in the future than last time.
- assertTrue(newExpiry > expiry);
- // Also check that it was not bumped as much as the last bump, because the
- // decay must be exponential.
- assertTrue(newExpiry - expiry < lastDifference);
- lastDifference = newExpiry - expiry;
- expiry = newExpiry;
- }
- fail("Relevance failed to go to the maximum value after " + steps + " bumps");
- }
-
- @Test
- public void testContinuity() {
- final long expiry = System.currentTimeMillis();
-
- // Relevance at expiry and after expiry should be the cap.
- final int relevanceBeforeMaxLifetime = RelevanceUtils.computeRelevanceForTargetDate(expiry,
- expiry - (RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS + 1_000_000));
- assertEquals(relevanceBeforeMaxLifetime, CAPPED_RELEVANCE);
- final int relevanceForMaxLifetime = RelevanceUtils.computeRelevanceForTargetDate(expiry,
- expiry - RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS);
- assertEquals(relevanceForMaxLifetime, CAPPED_RELEVANCE);
-
- // If the max relevance is reached at the cap lifetime, one millisecond less than this
- // should be very close. Strictly speaking this is a bit brittle, but it should be
- // good enough for the purposes of the memory store.
- final int relevanceForOneMillisecLessThanCap = RelevanceUtils.computeRelevanceForTargetDate(
- expiry, expiry - RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS + 1);
- assertTrue(relevanceForOneMillisecLessThanCap <= CAPPED_RELEVANCE);
- assertTrue(relevanceForOneMillisecLessThanCap >= CAPPED_RELEVANCE - 10);
-
- // Likewise the relevance one millisecond before expiry should be very close to 0. It's
- // fine if it rounds down to 0.
- final int relevanceOneMillisecBeforeExpiry = RelevanceUtils.computeRelevanceForTargetDate(
- expiry, expiry - 1);
- assertTrue(relevanceOneMillisecBeforeExpiry <= 10);
- assertTrue(relevanceOneMillisecBeforeExpiry >= 0);
-
- final int relevanceAtExpiry = RelevanceUtils.computeRelevanceForTargetDate(expiry, expiry);
- assertEquals(relevanceAtExpiry, 0);
- final int relevanceAfterExpiry = RelevanceUtils.computeRelevanceForTargetDate(expiry,
- expiry + 1_000_000);
- assertEquals(relevanceAfterExpiry, 0);
- }
-
- // testIncreaseRelevance makes sure bumping the expiry continuously always yields a
- // monotonically increasing date as a side effect, but this tests that the relevance (as
- // opposed to the expiry date) increases monotonically with increasing periods.
- @Test
- public void testMonotonicity() {
- // Hopefully the relevance is granular enough to give a different value for every one
- // of this number of steps.
- final int steps = 40;
- final long expiry = System.currentTimeMillis();
-
- int lastRelevance = -1;
- for (int i = 0; i < steps; ++i) {
- final long date = expiry - i * (RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS / steps);
- final int relevance = RelevanceUtils.computeRelevanceForTargetDate(expiry, date);
- assertTrue(relevance > lastRelevance);
- lastRelevance = relevance;
- }
- }
-}
diff --git a/packages/NetworkStack/tests/unit/src/com/android/server/util/SharedLogTest.java b/packages/NetworkStack/tests/unit/src/com/android/server/util/SharedLogTest.java
deleted file mode 100644
index b1db051..0000000
--- a/packages/NetworkStack/tests/unit/src/com/android/server/util/SharedLogTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.util;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.net.util.SharedLog;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.ByteArrayOutputStream;
-import java.io.PrintWriter;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class SharedLogTest {
- private static final String TIMESTAMP_PATTERN = "\\d{2}:\\d{2}:\\d{2}";
- private static final String TIMESTAMP = "HH:MM:SS";
-
- @Test
- public void testBasicOperation() {
- final SharedLog logTop = new SharedLog("top");
- logTop.mark("first post!");
-
- 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");
- logTop.log("still logging");
- logLevel3.log("3 >> 2");
- logLevel2a.mark("ok: last post");
-
- 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",
- " - [twoA] MARK ok: last post",
- };
- // Verify the logs are all there and in the correct order.
- verifyLogLines(expected, logTop);
-
- // In fact, because they all share the same underlying LocalLog,
- // every subcomponent SharedLog's dump() is identical.
- verifyLogLines(expected, logLevel2a);
- verifyLogLines(expected, logLevel2b);
- verifyLogLines(expected, logLevel3);
- }
-
- private static void verifyLogLines(String[] expected, SharedLog log) {
- final ByteArrayOutputStream ostream = new ByteArrayOutputStream();
- final PrintWriter pw = new PrintWriter(ostream, true);
- log.dump(null, pw, null);
-
- final String dumpOutput = ostream.toString();
- assertTrue(dumpOutput != null);
- assertTrue(!"".equals(dumpOutput));
-
- final String[] lines = dumpOutput.split("\n");
- assertEquals(expected.length, lines.length);
-
- for (int i = 0; i < expected.length; i++) {
- String got = lines[i];
- String want = expected[i];
- assertTrue(String.format("'%s' did not contain '%s'", got, want), got.endsWith(want));
- assertTrue(String.format("'%s' did not contain a %s timestamp", got, TIMESTAMP),
- got.replaceFirst(TIMESTAMP_PATTERN, TIMESTAMP).contains(TIMESTAMP));
- }
- }
-}
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 0116d0d..4cedc93 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Inligtingruiling"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Draadlose skermsertifisering"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Aktiveer Wi-Fi-woordryke aanmelding"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Regulering van Wi-Fi-opsporing"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobiele data is altyd aktief"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardewareversnelling vir verbinding"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Wys Bluetooth-toestelle sonder name"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Kon nie koppel nie"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Wys opsies vir draadlose skermsertifisering"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Verhoog Wi-Fi-aantekeningvlak, wys per SSID RSSI in Wi‑Fi-kieser"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Verlaag batteryverbruik en verbeter netwerk se werkverrigting"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Beperk"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Onbeperk"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Loggerbuffer se groottes"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index b914b07..3b7abdb 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"አውታረ መረብ"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"የገመድ አልባ ማሳያ እውቅና ማረጋገጫ"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"የWi‑Fi ተጨማሪ ቃላት ምዝግብ ማስታወሻ መያዝ"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi scan throttling"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"የተንቀሳቃሽ ስልክ ውሂብ ሁልጊዜ ገቢር ነው"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"የሃርድዌር ማቀላጠፊያን በማስተሳሰር ላይ"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"የብሉቱዝ መሣሪያዎችን ያለ ስሞች አሳይ"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"መገናኘት አልተቻለም"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"የገመድ አልባ ማሳያ እውቅና ማረጋገጫ አማራጮችን አሳይ"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"የWi‑Fi ምዝግብ ማስታወሻ አያያዝ ደረጃ ጨምር፣ በWi‑Fi መምረጫ ውስጥ በአንድ SSID RSSI አሳይ"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"የባትሪ መላሸቅን ይቀንሳል እንዲሁም የአውታረ መረብ አፈጻጸም ብቃትን ያሻሽላል"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"የሚለካ"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"ያልተለካ"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"የምዝግብ ማስታወሻ ያዥ መጠኖች"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 0a9edb5..df1ec30 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"الشبكات"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"شهادة عرض شاشة لاسلكي"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"تفعيل تسجيل Wi‑Fi Verbose"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"تقييد البحث عن شبكات Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"بيانات الجوّال نشطة دائمًا"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"تسريع الأجهزة للتوصيل"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"عرض أجهزة البلوتوث بدون أسماء"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"تعذّر الاتصال"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"عرض خيارات شهادة عرض شاشة لاسلكي"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"زيادة مستوى تسجيل Wi-Fi، وعرض لكل SSID RSSI في منتقي Wi-Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"لتقليل استنفاد البطارية وتحسين أداء الشبكة."</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"تفرض تكلفة استخدام"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"بدون قياس"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"أحجام ذاكرة التخزين المؤقت للتسجيل"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index fddfb05..9c25b66 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"নেটৱৰ্কিং"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"বেতাঁৰ ডিছপ্লে’ প্ৰমাণীকৰণ"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"ৱাই-ফাই ভাৰ্ব\'ছ লগিং সক্ষম কৰক"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"ৱাই-ফাই স্কেনৰ নিয়ন্ত্ৰণ"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"ম’বাইল ডেটা সদা-সক্ৰিয়"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"টেডাৰিং হাৰ্ডৱেৰ ত্বৰণ"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"নামবিহীন ব্লুটুথ ডিভাইচসমূহ দেখুৱাওক"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"সংযোগ কৰিব পৰা নগ\'ল"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"বেতাঁৰ ডিছপ্লে’ প্ৰমাণপত্ৰৰ বাবে বিকল্পসমূহ দেখুৱাওক"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"ৱাই-ফাই লগিঙৰ মাত্ৰা বঢ়াওক, Wi‑Fi পিকাৰত প্ৰতি SSID RSSI দেখুৱাওক"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"বেটাৰীৰ খৰচ কমায় আৰু নেটৱৰ্কৰ কাৰ্যক্ষমতা বৃদ্ধি কৰে"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"নিৰিখ-নিৰ্দিষ্ট"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"নিৰিখ অনিৰ্দিষ্ট"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"লগাৰৰ বাফাৰৰ আকাৰ"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 642b81c..3a6d301 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Şəbəkələşmə"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Simsiz displey sertifikatlaşması"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi‑Fi Çoxsözlü Girişə icazə verin"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi skanlamasının tənzimlənməsi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobil data həmişə aktiv"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Birləşmə üçün avadanlıq akselerasiyası"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Bluetooth cihazlarını adsız göstərin"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Qoşulmaq mümkün olmadı"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Simsiz displey sertifikatlaşması üçün seçimləri göstərir"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi giriş səviyyəsini qaldırın, Wi‑Fi seçəndə hər SSID RSSI üzrə göstərin"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Batareya istifadəsini azaldır & şəbəkə performansını yaxşılaşdırır"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Ödənişli"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Limitsiz"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Logger bufer ölçüləri"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 4fea77a..a8808fa 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Umrežavanje"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Sertifikacija bežičnog ekrana"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Omogući detaljniju evidenciju za Wi‑Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Usporavanje Wi-Fi skeniranja"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobilni podaci su uvek aktivni"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardversko ubrzanje privezivanja"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Prikaži Bluetooth uređaje bez naziva"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Povezivanje nije uspelo"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Prikaz opcija za sertifikaciju bežičnog ekrana"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Povećava nivo evidentiranja za Wi‑Fi. Prikaz po SSID RSSI-u u biraču Wi‑Fi mreže"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Smanjuje potrošnju baterije i poboljšava učinak mreže"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Sa ograničenjem"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Bez ograničenja"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Veličine bafera podataka u programu za evidentiranje"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 5013300..05d9a5d 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -35,7 +35,7 @@
<string name="wifi_not_in_range" msgid="1136191511238508967">"Не ў зоне дасягальнасці"</string>
<string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Не будзе аўтаматычна падключацца"</string>
<string name="wifi_no_internet" msgid="4663834955626848401">"Няма доступу да інтэрнэту"</string>
- <string name="saved_network" msgid="4352716707126620811">"Хто захаваў: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="saved_network" msgid="4352716707126620811">"Захавана праз: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="connected_via_network_scorer" msgid="5713793306870815341">"Аўтаматычна падключана праз %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7867260222020343104">"Аўтаматычна падключана праз пастаўшчыка паслугі ацэнкі сеткі"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Падключана праз %1$s"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Сеткі"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Сертыфікацыя бесправаднога экрана"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Уключыць падрабязны журнал Wi‑Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Рэгуляванне пошуку сетак Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Мабільная перадача даных заўсёды актыўная"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Апаратнае паскарэнне ў рэжыме мадэма"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Паказваць прылады Bluetooth без назваў"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Не атрымалася падключыцца"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Паказаць опцыі сертыфікацыі бесправаднога экрана"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Пры выбары сеткі Wi-Fi указваць у журнале RSSI для кожнага SSID"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Зніжае расход зараду акумулятара і павышае прадукцыйнасць мабільных сетак"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Сетка з улікам трафіка"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Сетка без уліку трафіка"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Памеры буфера журнала"</string>
@@ -407,7 +409,7 @@
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"ідзе зарадка"</string>
<string name="battery_info_status_discharging" msgid="310932812698268588">"Не зараджаецца"</string>
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Падключана да сеткі сілкавання, зарадзіць зараз немагчыма"</string>
- <string name="battery_info_status_full" msgid="2824614753861462808">"Поўная"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Акумулятар зараджаны"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Кантралюецца адміністратарам"</string>
<string name="disabled" msgid="9206776641295849915">"Адключанае"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Дазволена"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 18bbc80..7fa5b06 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Мрежи"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Безжичен дисплей"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"„Многословно“ регистр. на Wi‑Fi: Актив."</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Ограничаване на сканирането за Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Винаги активни мобилни данни"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Хардуерно ускорение за тетъринга"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Показване на устройствата с Bluetooth без имена"</string>
@@ -242,10 +243,11 @@
<string name="private_dns_mode_off" msgid="8236575187318721684">"Изкл."</string>
<string name="private_dns_mode_opportunistic" msgid="8314986739896927399">"Автоматично"</string>
<string name="private_dns_mode_provider" msgid="8354935160639360804">"Име на хоста на доставчика на частния DNS"</string>
- <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Въведете името на хоста на DNS доставчика"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Въведете име на хоста на DNS доставчика"</string>
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Не можа да се установи връзка"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Показване на опциите за сертифициране на безжичния дисплей"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"По-подробно регистр. на Wi‑Fi – данни за RSSI на SSID в инстр. за избор на Wi‑Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Намалява изразходването на батерията и подобрява ефективността на мрежата"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"С отчитане"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Без отчитане"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Размери на регистрац. буфери"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 9f9bf04..bdbde46 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"নেটওয়ার্কিং"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"ওয়্যারলেস ডিসপ্লে সার্টিফিকেশন"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"ওয়াই-ফাই ভারবোস লগিং সক্ষম করুন"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"ওয়াই-ফাই স্ক্যান থ্রোটলিং"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"মোবাইল ডেটা সব সময় সক্রিয় থাক"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"টিথারিং হার্ডওয়্যার অ্যাক্সিলারেশন"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"নামহীন ব্লুটুথ ডিভাইসগুলি দেখুন"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"কানেক্ট করা যায়নি"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"ওয়্যারলেস প্রদর্শন সার্টিফিকেশন জন্য বিকল্পগুলি দেখান"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"ওয়াই-ফাই লগিং স্তর বাড়ান, ওয়াই-ফাই চয়নকারীতে SSID RSSI অনুযায়ী দেখান"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"ব্যাটারির খরচ কমায় এবং নেটওয়ার্কের পারফর্ম্যান্স উন্নত করে"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"মিটার্ড"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"পরিমাপ করা নয়"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"লগার বাফারের আকারগুলি"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index bbc89d4..02b2a8b 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -21,9 +21,9 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ne može skenirati mreže"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Nema"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"Ništa"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Sačuvano"</string>
- <string name="wifi_disconnected" msgid="8085419869003922556">"Veza je prekinuta"</string>
+ <string name="wifi_disconnected" msgid="8085419869003922556">"Nije povezano"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogućeno"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Greška u konfiguraciji IP-a"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="5168315140978066096">"Niste povezani zbog slabog kvaliteta mreže"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Umrežavanje"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certifikacija bežičnog prikaza"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Omogući detaljni zapisnik za WiFi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Reguliranje skeniranja WiFi mreže"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Prijenos podataka na mobilnoj mreži je uvijek aktivan"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardversko ubrzavanje za povezivanje putem mobitela"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Prikaži Bluetooth uređaje bez naziva"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Povezivanje nije uspjelo"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Prikaz opcija za certifikaciju bežičnog prikaza"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Povećani nivo zapisnika za WiFi. Prikaz po SSID RSSI-ju u Biraču WiFi-ja"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Smanjuje potrošnju baterije i poboljšava performanse mreže"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"S naplatom"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Mreža bez naplate"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Veličine međumemorije zapisnika"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 8a4ab91..d464256 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -155,7 +155,7 @@
<string name="tts_settings" msgid="8186971894801348327">"Configuració de text a parla"</string>
<string name="tts_settings_title" msgid="1237820681016639683">"Sortida de text a parla"</string>
<string name="tts_default_rate_title" msgid="6030550998379310088">"Velocitat de parla"</string>
- <string name="tts_default_rate_summary" msgid="4061815292287182801">"Velocitat de lectura del text"</string>
+ <string name="tts_default_rate_summary" msgid="4061815292287182801">"Velocitat a què s\'enuncia el text"</string>
<string name="tts_default_pitch_title" msgid="6135942113172488671">"To"</string>
<string name="tts_default_pitch_summary" msgid="1944885882882650009">"Afecta el to de la veu sintetitzada"</string>
<string name="tts_default_lang_title" msgid="8018087612299820556">"Idioma"</string>
@@ -179,7 +179,7 @@
<string name="tts_engine_preference_section_title" msgid="448294500990971413">"Motor preferent"</string>
<string name="tts_general_section_title" msgid="4402572014604490502">"General"</string>
<string name="tts_reset_speech_pitch_title" msgid="5789394019544785915">"Restableix el to de la veu"</string>
- <string name="tts_reset_speech_pitch_summary" msgid="8700539616245004418">"Restableix el to predeterminat amb què es llegeix el text."</string>
+ <string name="tts_reset_speech_pitch_summary" msgid="8700539616245004418">"Restableix a predeterminat el to amb què s\'enuncia el text."</string>
<string-array name="tts_rate_entries">
<item msgid="6695494874362656215">"Molt lenta"</item>
<item msgid="4795095314303559268">"Lenta"</item>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Xarxes"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certificació de pantalla sense fil"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Activa el registre Wi‑Fi detallat"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Regulació de la cerca de xarxes Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Dades mòbils sempre actives"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Acceleració per maquinari per a compartició de xarxa"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Mostra els dispositius Bluetooth sense el nom"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"No s\'ha pogut connectar"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostra les opcions per a la certificació de pantalla sense fil"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Augmenta nivell de registre Wi‑Fi, mostra\'l per SSID RSSI al selector de Wi‑Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Redueix el consum de bateria i millora el rendiment de la xarxa"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"D\'ús mesurat"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"D\'ús no mesurat"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Mides de la mem. intermèdia del registrador"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 0163382..5256399 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Sítě"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certifikace bezdrát. displeje"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Podrobné protokolování Wi‑Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Přibrždění vyhledávání Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobilní data jsou vždy aktivní"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardwarová akcelerace tetheringu"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Zobrazovat zařízení Bluetooth bez názvů"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Nelze se připojit"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Zobrazit možnosti certifikace bezdrátového displeje"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Zvýšit úroveň protokolování Wi‑Fi zobrazenou v SSID a RSSI při výběru sítě Wi‑Fi."</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Snižuje vyčerpávání baterie a vylepšuje výkon sítě"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Měřená"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Neměřená"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Vyrovnávací paměť protokol. nástroje"</string>
@@ -419,8 +421,8 @@
<item msgid="8934126114226089439">"50 %"</item>
<item msgid="1286113608943010849">"100 %"</item>
</string-array>
- <string name="charge_length_format" msgid="8978516217024434156">"před <xliff:g id="ID_1">%1$s</xliff:g>"</string>
- <string name="remaining_length_format" msgid="7886337596669190587">"Zbývající čas: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="charge_length_format" msgid="8978516217024434156">"Před <xliff:g id="ID_1">%1$s</xliff:g>"</string>
+ <string name="remaining_length_format" msgid="7886337596669190587">"zbývá: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="screen_zoom_summary_small" msgid="5867245310241621570">"Malé"</string>
<string name="screen_zoom_summary_default" msgid="2247006805614056507">"Výchozí"</string>
<string name="screen_zoom_summary_large" msgid="4835294730065424084">"Velké"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 04ef33d..1884f64 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -78,7 +78,7 @@
<string name="bluetooth_active_battery_level_untethered" msgid="6662649951391456747">"Aktivt – venstre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batteri. Højre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri"</string>
<string name="bluetooth_battery_level" msgid="1447164613319663655">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
<string name="bluetooth_battery_level_untethered" msgid="5974406100211667177">"Venstre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batteri. Højre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri"</string>
- <string name="bluetooth_active_no_battery_level" msgid="8380223546730241956">"Aktivt"</string>
+ <string name="bluetooth_active_no_battery_level" msgid="8380223546730241956">"Aktiv"</string>
<string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Medielyd"</string>
<string name="bluetooth_profile_headset" msgid="7815495680863246034">"Telefonopkald"</string>
<string name="bluetooth_profile_opp" msgid="9168139293654233697">"Filoverførsel"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Netværk"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certificering af trådløs skærm"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Aktivér detaljeret Wi-Fi-logføring"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Begrænsning af Wi-Fi-scanning"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobildata er altid aktiveret"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardwareacceleration ved netdeling"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Vis Bluetooth-enheder uden navne"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Der kunne ikke oprettes forbindelse"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Vis valgmuligheder for certificering af trådløs skærm"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Øg mængden af Wi‑Fi-logføring. Vis opdelt efter SSID RSSI i Wi‑Fi-vælgeren"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Reducerer batteriforbruget og forbedrer netværkets effektivitet"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Forbrugsafregnet"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Ikke forbrugsafregnet"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Størrelser for Logger-buffer"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index e705d82..0347f2d 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -113,7 +113,7 @@
<string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Koppeln"</string>
<string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"KOPPELN"</string>
<string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Abbrechen"</string>
- <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Über die Kopplung kann auf deine Kontakte und auf deinen Anrufverlauf zugegriffen werden, wenn eine Verbindung besteht."</string>
+ <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"Über die Kopplung kann auf deine Kontakte und auf deine Anrufliste zugegriffen werden, wenn eine Verbindung besteht."</string>
<string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"Kopplung mit <xliff:g id="DEVICE_NAME">%1$s</xliff:g> war nicht möglich."</string>
<string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"Kopplung mit <xliff:g id="DEVICE_NAME">%1$s</xliff:g> war nicht möglich, weil die eingegebene PIN oder der Zugangscode falsch ist."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"Kommunikation mit <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ist nicht möglich."</string>
@@ -153,7 +153,7 @@
<string name="launch_defaults_some" msgid="313159469856372621">"Einige Standardeinstellungen festgelegt"</string>
<string name="launch_defaults_none" msgid="4241129108140034876">"Keine Standardeinstellungen festgelegt"</string>
<string name="tts_settings" msgid="8186971894801348327">"Sprachausgabe"</string>
- <string name="tts_settings_title" msgid="1237820681016639683">"Sprachausgabe-Einstellungen"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Sprachausgabe"</string>
<string name="tts_default_rate_title" msgid="6030550998379310088">"Sprechgeschwindigkeit"</string>
<string name="tts_default_rate_summary" msgid="4061815292287182801">"Geschwindigkeit, mit der der Text gesprochen wird"</string>
<string name="tts_default_pitch_title" msgid="6135942113172488671">"Tonlage"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Netzwerke"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Zertifizierung für kabellose Übertragung"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Ausführliche WLAN-Protokollierung aktivieren"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Drosselung der WLAN-Suche"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobile Datennutzung immer aktiviert"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardwarebeschleunigung für Tethering"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Bluetooth-Geräte ohne Namen anzeigen"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Verbindung nicht möglich"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Optionen zur Zertifizierung für kabellose Übertragung anzeigen"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"WLAN-Protokollierungsebene erhöhen, in WiFi Picker pro SSID RSSI anzeigen"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Verringert den Akkuverbrauch und verbessert die Netzwerkleistung"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Kostenpflichtig"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Kostenlos"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Logger-Puffergrößen"</string>
@@ -326,7 +328,7 @@
<string name="app_process_limit_title" msgid="4280600650253107163">"Limit für Hintergrundprozesse"</string>
<string name="show_all_anrs" msgid="4924885492787069007">"Absturzmeldungen für Hintergrund-Apps anzeigen"</string>
<string name="show_all_anrs_summary" msgid="6636514318275139826">"Bei Abstürzen von Hintergrund-Apps \"App reagiert nicht\"-Dialog anzeigen"</string>
- <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Benachrichtigungskanal-Warnungen anzeigen"</string>
+ <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Benachrichtigungskanal- Warnungen anzeigen"</string>
<string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Bei Benachrichtigungen ohne gültigen Kanal wird eine Warnung angezeigt"</string>
<string name="force_allow_on_external" msgid="3215759785081916381">"Sperrung des externen Speichers für alle Apps aufheben"</string>
<string name="force_allow_on_external_summary" msgid="3640752408258034689">"Jede App kann, ungeachtet der Manifestwerte, in den externen Speicher geschrieben werden"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 031cd0d..b1b1be6 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Δικτύωση"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Πιστοποίηση ασύρματης οθόνης"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Ενεργοποίηση λεπτομερ. καταγραφής Wi-Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Περιορισμός σάρωσης Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Πάντα ενεργά δεδομένα κινητής τηλεφωνίας"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Σύνδεση επιτάχυνσης υλικού"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Εμφάνιση συσκευών Bluetooth χωρίς ονόματα"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Δεν ήταν δυνατή η σύνδεση"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Εμφάνιση επιλογών για πιστοποίηση ασύρματης οθόνης"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Αύξηση επιπέδου καταγ. Wi-Fi, εμφάνιση ανά SSID RSSI στο εργαλείο επιλογής Wi-Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Περιορίζει την κατανάλωση της μπαταρίας και βελτιώνει την απόδοση του δικτύου"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Μέτρηση με βάση τη χρήση"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Χωρίς μέτρηση με βάση τη χρήση"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Μέγεθος προσωρινής μνήμης για τη λειτουργία καταγραφής"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 5728800..57f5d5a 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Networking"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Wireless display certification"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Enable Wi‑Fi verbose logging"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi scan throttling"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobile data always active"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Tethering hardware acceleration"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Show Bluetooth devices without names"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Couldn\'t connect"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Reduces battery drain and improves network performance"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Metered"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Unmetered"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Logger buffer sizes"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 5728800..57f5d5a 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Networking"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Wireless display certification"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Enable Wi‑Fi verbose logging"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi scan throttling"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobile data always active"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Tethering hardware acceleration"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Show Bluetooth devices without names"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Couldn\'t connect"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Reduces battery drain and improves network performance"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Metered"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Unmetered"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Logger buffer sizes"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 5728800..57f5d5a 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Networking"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Wireless display certification"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Enable Wi‑Fi verbose logging"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi scan throttling"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobile data always active"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Tethering hardware acceleration"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Show Bluetooth devices without names"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Couldn\'t connect"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Reduces battery drain and improves network performance"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Metered"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Unmetered"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Logger buffer sizes"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 5728800..57f5d5a 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Networking"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Wireless display certification"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Enable Wi‑Fi verbose logging"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi scan throttling"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobile data always active"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Tethering hardware acceleration"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Show Bluetooth devices without names"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Couldn\'t connect"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Reduces battery drain and improves network performance"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Metered"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Unmetered"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Logger buffer sizes"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 460442b..e5a0bda 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Networking"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Wireless display certification"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Enable Wi‑Fi Verbose Logging"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi scan throttling"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobile data always active"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Tethering hardware acceleration"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Show Bluetooth devices without names"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Couldn\'t connect"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Reduces battery drain & improves network performance"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Metered"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Unmetered"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Logger buffer sizes"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 22d39c5..beb1ac5 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -141,9 +141,9 @@
<string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicaciones eliminadas"</string>
<string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicaciones y usuarios eliminados"</string>
<string name="data_usage_ota" msgid="5377889154805560860">"Actualizaciones del sistema"</string>
- <string name="tether_settings_title_usb" msgid="6688416425801386511">"Conexión mediante USB"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Conexión USB"</string>
<string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot portátil"</string>
- <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Conexión mediante Bluetooth"</string>
+ <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Conexión Bluetooth"</string>
<string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Compartir conexión"</string>
<string name="tether_settings_title_all" msgid="8356136101061143841">"Hotspots y dispositivos portátiles"</string>
<string name="managed_user_title" msgid="8109605045406748842">"Todas las apps de trabajo"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Redes"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certificación de pantalla inalámbrica"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Habilitar registro detallado de Wi-Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Limitación de búsqueda de Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Datos móviles siempre activados"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Aceleración de hardware de conexión mediante dispositivo portátil"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Mostrar dispositivos Bluetooth sin nombre"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"No se pudo establecer conexión"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opciones de certificación de pantalla inalámbrica"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar nivel de registro Wi-Fi; mostrar por SSID RSSI en el selector de Wi-Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Reduce el consumo de batería y mejora el rendimiento de la red"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Con uso medido"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Sin tarifa plana"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Tamaños de búfer de Logger"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 533296c..e1277d8 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Redes"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certificación de pantalla inalámbrica"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Habilitar registro de Wi-Fi detallado"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Limitación de búsqueda de redes Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Datos móviles siempre activos"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Aceleración por hardware para conexión compartida"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Mostrar dispositivos Bluetooth sin nombre"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"No se ha podido establecer la conexión"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opciones para la certificación de la pantalla inalámbrica"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar el nivel de registro de Wi-Fi y mostrar por SSID RSSI en el selector Wi-Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Reduce el consumo de batería y mejora el rendimiento de las redes"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Medida"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"No medida"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Tamaños del búfer para registrar"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index e5e8f56..090b28d 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Võrgundus"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Juhtmeta ekraaniühenduse sertifitseerimine"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Luba WiFi sõnaline logimine"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"WiFi-skannimise ahendamine"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Hoia mobiilne andmeside alati aktiivne"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Ühenduse jagamise riistvaraline kiirendus"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Kuva ilma nimedeta Bluetoothi seadmed"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Ühendamine ebaõnnestus"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Juhtmeta ekraaniühenduse sertifitseerimisvalikute kuvamine"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Suurenda WiFi logimistaset, kuva WiFi valijas SSID RSSI järgi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Aeglustab aku tühjenemist ja parandab võrgu toimivust"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Mahupõhine"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Mittemahupõhine"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Logija puhvri suurused"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 447b738..d2e3a38 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Sareak"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Hari gabe bistaratzeko ziurtagiria"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Gaitu wifi-sareetan saioa hasteko modu xehatua"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wifi-sareen bilaketaren muga"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Datu-konexioa beti aktibo"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Konexioa partekatzeko hardwarearen azelerazioa"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Erakutsi Bluetooth gailuak izenik gabe"</string>
@@ -246,8 +247,9 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Ezin izan da konektatu"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Erakutsi hari gabe bistaratzeko ziurtagiriaren aukerak"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Erakutsi datu gehiago wifi-sareetan saioa hastean. Erakutsi sarearen identifikatzailea eta seinalearen indarra wifi-sareen hautagailuan."</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Bateria gutxiago kontsumituko da, eta sarearen errendimendua hobetuko."</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Sare neurtua"</string>
- <string name="wifi_unmetered_label" msgid="6124098729457992931">"Sare ez-mugatua"</string>
+ <string name="wifi_unmetered_label" msgid="6124098729457992931">"Neurtu gabeko sarea"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Erregistroen buffer-tamainak"</string>
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Hautatu erregistroen buffer-tamainak"</string>
<string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Erregistro iraunkorraren biltegia garbitu nahi duzu?"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index c8f99a7..6632752 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"شبکه"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"گواهینامه نمایش بیسیم"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"فعال کردن گزارشگیری طولانی Wi‑Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"محدود کردن اسکن کردن Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"داده تلفن همراه همیشه فعال باشد"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"شتاب سختافزاری اتصال به اینترنت با تلفن همراه"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"نمایش دستگاههای بلوتوث بدون نام"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"متصل نشد"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"نمایش گزینهها برای گواهینامه نمایش بیسیم"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"افزایش سطح گزارشگیری Wi‑Fi، نمایش به ازای SSID RSSI در انتخابکننده Wi‑Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"تخلیه باتری راکاهش میدهد و عملکرد شبکه را بهبود میبخشد"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"محدودشده"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"محدودنشده"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"اندازههای حافظه موقت ثبتکننده"</string>
@@ -454,8 +456,8 @@
<string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"هرگز"</string>
<string name="zen_interruption_level_priority" msgid="2078370238113347720">"فقط اولویتدار"</string>
<string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
- <string name="zen_alarm_warning_indef" msgid="3007988140196673193">"صدای زنگ بعدیتان را در ساعت <xliff:g id="WHEN">%1$s</xliff:g> نخواهید شنید، مگر اینکه قبل از آن ساعت، این تنظیم را خاموش کنید"</string>
- <string name="zen_alarm_warning" msgid="6236690803924413088">"صدای زنگ بعدیتان را در ساعت <xliff:g id="WHEN">%1$s</xliff:g> نخواهید شنید"</string>
+ <string name="zen_alarm_warning_indef" msgid="3007988140196673193">"زنگ بعدیتان را در ساعت <xliff:g id="WHEN">%1$s</xliff:g> نخواهید شنید، مگر اینکه قبلاز آن ساعت، این تنظیم را خاموش کنید"</string>
+ <string name="zen_alarm_warning" msgid="6236690803924413088">"زنگ بعدیتان را در ساعت <xliff:g id="WHEN">%1$s</xliff:g> نخواهید شنید"</string>
<string name="alarm_template" msgid="4996153414057676512">"ساعت <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<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>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index b166cb0..0d522c4 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Yhteysominaisuudet"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Langattoman näytön sertifiointi"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Käytä Wi-Fin laajennettua lokikirjausta"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi-haun rajoitus"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobiilidata aina käytössä"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Laitteistokiihdytyksen yhteyden jakaminen"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Näytä nimettömät Bluetooth-laitteet"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Ei yhteyttä"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Näytä langattoman näytön sertifiointiin liittyvät asetukset."</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Lisää Wi‑Fin lokikirjaustasoa, näytä SSID RSSI -kohtaisesti Wi‑Fi-valitsimessa."</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Vähentää virrankulutusta ja parantaa verkon toimintaa"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Maksullinen"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Maksuton"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Lokipuskurien koot"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 9983337..1dde863 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -153,7 +153,7 @@
<string name="launch_defaults_some" msgid="313159469856372621">"Certaines préférences par défaut définies"</string>
<string name="launch_defaults_none" msgid="4241129108140034876">"Aucune préférence par défaut définie"</string>
<string name="tts_settings" msgid="8186971894801348327">"Synthèse vocale"</string>
- <string name="tts_settings_title" msgid="1237820681016639683">"Sortie de la synthèse vocale"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Synthèse vocale"</string>
<string name="tts_default_rate_title" msgid="6030550998379310088">"Cadence"</string>
<string name="tts_default_rate_summary" msgid="4061815292287182801">"Vitesse à laquelle le texte est énoncé"</string>
<string name="tts_default_pitch_title" msgid="6135942113172488671">"Ton"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Réseautage"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certification de l\'affichage sans fil"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Autoriser enreg. données Wi-Fi détaillées"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Limiter la recherche de réseaux Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Données cellulaires toujours actives"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Accélération matérielle pour le partage de connexion"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Afficher les appareils Bluetooth sans nom"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Impossible de se connecter"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Afficher les options pour la certification d\'affichage sans fil"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Détailler davantage les données Wi-Fi, afficher par SSID RSSI dans sélect. Wi-Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Réduit l\'utilisation de la pile et améliore les performances réseau"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Facturé à l\'usage"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Non mesuré"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Tailles des mémoires tampons d\'enregistreur"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index e2bc2fe..3aa8db9 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Mise en réseau"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certification affichage sans fil"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Autoriser l\'enregistrement d\'infos Wi-Fi détaillées"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Limiter la recherche Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Données mobiles toujours actives"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Accélération matérielle pour le partage de connexion"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Afficher les appareils Bluetooth sans nom"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Impossible de se connecter"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Afficher les options pour la certification de l\'affichage sans fil"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Détailler les infos Wi-Fi, afficher par RSSI de SSID dans l\'outil de sélection Wi-Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Réduit la décharge de la batterie et améliore les performances du réseau"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Facturé à l\'usage"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Non facturé à l\'usage"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Tailles des tampons de l\'enregistreur"</string>
@@ -404,7 +406,7 @@
<string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la charge complète"</string>
<string name="battery_info_status_unknown" msgid="196130600938058547">"Inconnu"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"Batterie en charge"</string>
- <string name="battery_info_status_charging_lower" msgid="8689770213898117994">"chargement…"</string>
+ <string name="battery_info_status_charging_lower" msgid="8689770213898117994">"en charge…"</string>
<string name="battery_info_status_discharging" msgid="310932812698268588">"Pas en charge"</string>
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"Appareil branché, mais impossible de le charger pour le moment"</string>
<string name="battery_info_status_full" msgid="2824614753861462808">"Pleine"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index f41be39..4bd1c78 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Redes"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certificado de visualización sen fíos"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Activar rexistro detallado da wifi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Limitación da busca de wifi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Datos móbiles sempre activados"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Aceleración de hardware para conexión compartida"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Mostrar dispositivos Bluetooth sen nomes"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Non se puido conectar"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostra opcións para o certificado de visualización sen fíos"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumenta o nivel de rexistro da wifi, móstrao por SSID RSSI no selector de wifi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Reduce o consumo de batería e mellora o rendemento da rede"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Sen tarifa plana"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Con tarifa plana"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Tamaño dos búfers do rexistrador"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index a411a05..cf0e201 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"નેટવર્કિંગ"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"વાયરલેસ ડિસ્પ્લે પ્રમાણન"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"વાઇ-ફાઇ વર્બોઝ લૉગિંગ ચાલુ કરો"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"વાઇ-ફાઇ સ્કૅનની ક્ષમતા મર્યાદિત કરવી"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"મોબાઇલ ડેટા હંમેશાં સક્રિય"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"ટિથરિંગ માટે હાર્ડવેર ગતિવૃદ્ધિ"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"નામ વિનાના બ્લૂટૂથ ઉપકરણો બતાવો"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"કનેક્ટ કરી શકાયું નથી"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"વાયરલેસ ડિસ્પ્લે પ્રમાણપત્ર માટેના વિકલ્પો બતાવો"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"વાઇ-ફાઇ લોગિંગ સ્તર વધારો, વાઇ-ફાઇ પીકરમાં SSID RSSI દીઠ બતાવો"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"બૅટરીનો ચાર્જ ઝડપથી ઓછો થવાનું ટાળે છે અને નેટવર્કની કાર્યક્ષમતામાં સુધારો કરે છે"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"મીટર કરેલ"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"મીટર ન કરેલ"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"લોગર બફર કદ"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 954c04a..8c6ff8e 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"नेटवर्किंग"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"वायरलेस डिसप्ले सर्टिफ़िकेशन"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"वाई-फ़ाई वर्बोस लॉगिंग चालू करें"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"वाई-फ़ाई के लिए स्कैन की संख्या कम करें"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"मोबाइल डेटा हमेशा चालू"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"हार्डवेयर से तेज़ी लाने के लिए टेदर करें"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"बिना नाम वाले ब्लूटूथ डिवाइस दिखाएं"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"कनेक्ट नहीं हो सका"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"वायरलेस डिसप्ले सर्टिफ़िकेशन के विकल्प दिखाएं"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"वाई-फ़ाई लॉगिंग का स्तर बढ़ाएं, वाई-फ़ाई पिकर में प्रति SSID RSSI दिखाएं"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"बैटरी की खपत कम और नेटवर्क की परफ़ॉर्मेंस बेहतर होती है"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"डेटा इस्तेमाल करने की सीमा तय की गई है"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"डेटा इस्तेमाल करने की सीमा तय नहीं की गई है"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"लॉगर बफ़र आकार"</string>
@@ -282,7 +284,7 @@
<string name="no_application" msgid="2813387563129153880">"कुछ भी नहीं"</string>
<string name="wait_for_debugger" msgid="1202370874528893091">"डीबगर का इंतज़ार करें"</string>
<string name="wait_for_debugger_summary" msgid="1766918303462746804">"डीबग किया गया ऐप्लिकेशन प्रोसेस के पहले डीबगर के अटैच होने का इंतज़ार करता है"</string>
- <string name="debug_input_category" msgid="1811069939601180246">"हिंदी में लिखें"</string>
+ <string name="debug_input_category" msgid="1811069939601180246">"इनपुट"</string>
<string name="debug_drawing_category" msgid="6755716469267367852">"ड्रॉइंग"</string>
<string name="debug_hw_drawing_category" msgid="6220174216912308658">"हार्डवेयर ऐक्सेलरेटेड रेंडरिंग"</string>
<string name="media_category" msgid="4388305075496848353">"मीडिया"</string>
@@ -434,7 +436,7 @@
<string name="active_input_method_subtypes" msgid="3596398805424733238">"टाइप करने की सक्रीय पद्धतियां"</string>
<string name="use_system_language_to_select_input_method_subtypes" msgid="5747329075020379587">"सिस्टम की भाषाओं का उपयोग करें"</string>
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> के लिए सेटिंग खोलने में विफल रहा"</string>
- <string name="ime_security_warning" msgid="4135828934735934248">"यह इनपुट विधि, पासवर्ड और क्रेडिट कार्ड नंबर जैसे निजी डेटा सहित लिखे जाने वाले सभी लेख को एकत्र कर सकती है. यह <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ऐप्लिकेशन से आती है. इस इनपुट विधि का उपयोग करें?"</string>
+ <string name="ime_security_warning" msgid="4135828934735934248">"इनपुट का यह तरीका, आपके पासवर्ड और क्रेडिट कार्ड नंबर जैसे निजी डेटा के साथ-साथ उस सभी डेटा को इकट्ठा कर सकता है जिसे आप लिखते हैं. यह <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ऐप्लिकेशन से आता है. इनपुट के इस तरीके का इस्तेमाल करें?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"नोट: पुनः बूट करने के बाद, यह ऐप्लिकेशन तब तक शुरू नहीं हो सकता है जब तक कि आप अपना फ़ोन अनलॉक ना कर लें"</string>
<string name="ims_reg_title" msgid="7609782759207241443">"IMS रजिस्ट्रेशन की स्थिति"</string>
<string name="ims_reg_status_registered" msgid="933003316932739188">"रजिस्टर है"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index ea4a688..6d957dc 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Umrežavanje"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certifikacija bežičnog prikaza"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Omogući opširnu prijavu na Wi-Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Usporavanje traženja Wi-Fija"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobilni podaci uvijek aktivni"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardversko ubrzanje za modemsko povezivanje"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Prikaži Bluetooth uređaje bez naziva"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Povezivanje nije moguće"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Prikaz opcija za certifikaciju bežičnog prikaza"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Povećana razina prijave na Wi‑Fi, prikaz po SSID RSSI-ju u Biraču Wi‑Fi-ja"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Smanjuje potrošnju baterije i poboljšava rad mreže"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"S ograničenim prometom"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Bez ograničenja prometa"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Veličine međuspremnika zapisnika"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index d4fc9a7..39b5da2 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Hálózatok"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Vezeték nélküli kijelző tanúsítványa"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Részletes Wi-Fi-naplózás engedélyezése"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi-Fi-hálózat szabályozása"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"A mobilhálózati kapcsolat mindig aktív"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Internetmegosztás hardveres gyorsítása"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Név nélküli Bluetooth-eszközök megjelenítése"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Nem sikerült kapcsolódni"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Vezeték nélküli kijelző tanúsítványával kapcsolatos lehetőségek megjelenítése"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi-naplózási szint növelése, RSSI/SSID megjelenítése a Wi‑Fi-választóban"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Csökkenti az akkumulátorhasználatot, és javítja a hálózat teljesítményét"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Forgalomkorlátos"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Nem forgalomkorlátos"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Naplózási puffer mérete"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 836b70d..bf58740 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -84,7 +84,7 @@
<string name="bluetooth_profile_opp" msgid="9168139293654233697">"Ֆայլերի փոխանցում"</string>
<string name="bluetooth_profile_hid" msgid="3680729023366986480">"Ներմուծման սարք"</string>
<string name="bluetooth_profile_pan" msgid="3391606497945147673">"Ինտերնետի հասանելիություն"</string>
- <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Կոնտակտի համօգտագործում"</string>
+ <string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Կոնտակտների փոխանակում"</string>
<string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Օգտագործել կոնտակտի համօգտագործման համար"</string>
<string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Ինտերնետ կապի տարածում"</string>
<string name="bluetooth_profile_map" msgid="1019763341565580450">"SMS հաղորդագրություններ"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Ցանց"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Անլար էկրանների հավաստագրում"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Միացնել Wi‑Fi մանրամասն գրանցամատյանները"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi-ի որոնման սահմանափակում"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Բջջային ինտերնետը միշտ ակտիվ է"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Սարքակազմի արագացման միացում"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Ցուցադրել Bluetooth սարքերն առանց անունների"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Չհաջողվեց միանալ"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Ցույց տալ անլար էկրանների հավաստագրման ընտրանքները"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Բարձրացնել մակարդակը, Wi‑Fi ընտրիչում ամեն մի SSID-ի համար ցույց տալ RSSI"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Նվազեցնում է մարտկոցի սպառումը և լավացնում ցանցի աշխատանքը"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Վճարովի թրաֆիկ"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Անսահմանափակ թրաֆիկ"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Մատյանի բուֆերի չափերը"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 1b8304e..0684b3d 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -159,7 +159,7 @@
<string name="tts_default_pitch_title" msgid="6135942113172488671">"Tinggi nada"</string>
<string name="tts_default_pitch_summary" msgid="1944885882882650009">"Memengaruhi nada ucapan yang disintesis"</string>
<string name="tts_default_lang_title" msgid="8018087612299820556">"Bahasa"</string>
- <string name="tts_lang_use_system" msgid="2679252467416513208">"Menggunakan bahasa sistem"</string>
+ <string name="tts_lang_use_system" msgid="2679252467416513208">"Gunakan bahasa sistem"</string>
<string name="tts_lang_not_selected" msgid="7395787019276734765">"Bahasa tidak dipilih"</string>
<string name="tts_default_lang_summary" msgid="5219362163902707785">"Menyetel suara spesifik bahasa untuk teks lisan"</string>
<string name="tts_play_example_title" msgid="7094780383253097230">"Dengarkan contoh"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Jaringan"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Sertifikasi layar nirkabel"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Aktifkan Pencatatan Log Panjang Wi-Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Pembatasan pemindaian Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Kuota selalu aktif"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Akselerasi hardware tethering"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Tampilkan perangkat Bluetooth tanpa nama"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Tidak dapat terhubung"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Tampilkan opsi untuk sertifikasi layar nirkabel"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Tingkatkan level pencatatan log Wi-Fi, tampilkan per SSID RSSI di Pemilih Wi‑Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Memperlambat kehabisan baterai & meningkatkan performa jaringan"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Berbayar"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Tidak berbayar"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Ukuran buffer pencatat log"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 8333a59..11d69571 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Netkerfi"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Vottun þráðlausra skjáa"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Kveikja á ítarlegri skráningu Wi-Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Hægja á Wi‑Fi leit"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Alltaf kveikt á farsímagögnum"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Vélbúnaðarhröðun fyrir tjóðrun"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Sýna Bluetooth-tæki án heita"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Tenging mistókst"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Sýna valkosti fyrir vottun þráðlausra skjáa"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Auka skráningarstig Wi-Fi, sýna RSSI fyrir hvert SSID í Wi-Fi vali"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Dregur úr rafhlöðunotkun og eykur netafköst"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Mæld notkun"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Notkun ekki mæld"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Annálsritastærðir biðminna"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index e1e06d2..c5176b0 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -83,7 +83,7 @@
<string name="bluetooth_profile_headset" msgid="7815495680863246034">"Telefonate"</string>
<string name="bluetooth_profile_opp" msgid="9168139293654233697">"Trasferimento file"</string>
<string name="bluetooth_profile_hid" msgid="3680729023366986480">"Dispositivo di input"</string>
- <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Accesso Internet"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Accesso a Internet"</string>
<string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Condivisione contatti"</string>
<string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Usa per condivisione contatti"</string>
<string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Condivisione connessione Internet"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Reti"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certificazione display wireless"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Attiva logging dettagliato Wi-Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Limitazione della ricerca di reti Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Dati mobili sempre attivi"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Tethering accelerazione hardware"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Mostra dispositivi Bluetooth senza nome"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Impossibile collegarsi"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostra opzioni per la certificazione display wireless"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumenta livello di logging Wi-Fi, mostra SSID RSSI nel selettore Wi-Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Riduce il consumo della batteria e migliora le prestazioni della rete"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"A consumo"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Non a consumo"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Dimensioni buffer logger"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 9352479..ead0bfb 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"תקשורת רשתות"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"אישור של תצוגת WiFi"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"הפעלת רישום מפורט של Wi‑Fi ביומן"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"ויסות סריקה לנקודות Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"חבילת הגלישה פעילה תמיד"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"שיפור מהירות באמצעות חומרה לצורך שיתוף אינטרנט בין ניידים"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"הצגת מכשירי Bluetooth ללא שמות"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"לא ניתן היה להתחבר"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"הצג אפשרויות עבור אישור של תצוגת WiFi"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"העלה את רמת הרישום של Wi‑Fi ביומן, הצג לכל SSID RSSI ב-Wi‑Fi Picker"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"מפחית את קצב התרוקנות הסוללה ומשפר את ביצועי הרשת"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"נמדדת"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"לא נמדדת"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"גדלי מאגר של יומן רישום"</string>
@@ -407,7 +409,7 @@
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"בטעינה"</string>
<string name="battery_info_status_discharging" msgid="310932812698268588">"לא בטעינה"</string>
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"המכשיר מחובר, אבל לא ניתן לטעון עכשיו"</string>
- <string name="battery_info_status_full" msgid="2824614753861462808">"מלא"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"מלאה"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"נמצא בשליטת מנהל מערכת"</string>
<string name="disabled" msgid="9206776641295849915">"מושבת"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"מורשה"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index b032aad..e5d29bf 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"ネットワーク"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"ワイヤレスディスプレイ認証"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi-Fi詳細ログの有効化"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi スキャン スロットリング"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"モバイルデータを常に ON にする"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"テザリング時のハードウェア アクセラレーション"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Bluetooth デバイスを名前なしで表示"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"接続できませんでした"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"ワイヤレスディスプレイ認証のオプションを表示"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fiログレベルを上げて、Wi-Fi選択ツールでSSID RSSIごとに表示します"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"電池の消耗が軽減され、ネットワーク パフォーマンスが改善されます"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"従量制"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"定額制"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ログバッファのサイズ"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 2c1d7c0..8b9b6b6 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"ქსელი"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"უსადენო ეკრანის სერტიფიცირება"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi‑Fi-ს დაწვრილებითი აღრიცხვის ჩართვა"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi სკანირების რეგულირება"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"მობილური ინტერნეტის ყოველთვის გააქტიურება"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"ტეტერინგის აპარატურული აჩქარება"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Bluetooth-მოწყობილობების ჩვენება სახელების გარეშე"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"დაკავშირება ვერ მოხერხდა"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"უსადენო ეკრანის სერტიფიცირების ვარიანტების ჩვენება"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi-ს აღრიცხვის დონის გაზრდა, Wi‑Fi ამომრჩეველში ყოველ SSID RSSI-ზე ჩვენება"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"ამცირებს ბატარეის ხარჯვას და აუმჯობესებს ქსელის მუშაობას"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"ლიმიტირებული"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"არალიმიტირებული"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ჟურნალიზაციის ბუფერის ზომები"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 5ae201e..1e57297 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Желі орнату"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Сымсыз дисплей сертификаты"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi‑Fi егжей-тегжейлі журналы"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi іздеуін шектеу"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Мобильдік деректер әрқашан қосулы"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Тетеринг режиміндегі аппараттық жеделдету"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Bluetooth құрылғыларын атаусыз көрсету"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Қосылмады"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Сымсыз дисплей сертификаты опцияларын көрсету"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi тіркеу деңгейін арттыру, Wi‑Fi таңдағанда әр SSID RSSI бойынша көрсету"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Батарея зарядының шығынын азайтады және желі жұмысын жақсартады."</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Трафик саналады"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Трафик саналмайды"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Журналға тіркеуші буферінің өлшемдері"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index ed00b68..fb09c2f 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"ការភ្ជាប់បណ្ដាញ"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"សេចក្តីបញ្ជាក់ការបង្ហាញឥតខ្សែ"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"បើកកំណត់ហេតុរៀបរាប់ Wi-Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"ការពន្យឺតការស្កេន Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"ទិន្នន័យទូរសព្ទចល័តដំណើរការជានិច្ច"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"ការបង្កើនល្បឿនផ្នែករឹងសម្រាប់ការភ្ជាប់"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"បង្ហាញឧបករណ៍ប្ល៊ូធូសគ្មានឈ្មោះ"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"មិនអាចភ្ជាប់បានទេ"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"បង្ហាញជម្រើសសម្រាប់សេចក្តីបញ្ជាក់ការបង្ហាញឥតខ្សែ"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"បង្កើនកម្រិតកំណត់ហេតុ Wi-Fi បង្ហាញក្នុង SSID RSSI ក្នុងកម្មវិធីជ្រើសរើស Wi-Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"កាត់បន្ថយការប្រើប្រាស់ថ្ម និងកែលម្អប្រតិបត្តិការបណ្ដាញ"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"មានការកំណត់"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"មិនមានការកំណត់"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ទំហំកន្លែងផ្ទុករបស់ logger"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 59adfb7..97eed0f 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"ನೆಟ್ವರ್ಕಿಂಗ್"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"ವೈರ್ಲೆಸ್ ಪ್ರದರ್ಶನ ಪ್ರಮಾಣೀಕರಣ"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi‑Fi ವೆರ್ಬೋಸ್ ಲಾಗಿಂಗ್ ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"ವೈ-ಫೈ ಸ್ಕ್ಯಾನ್ ನಿರ್ಬಂಧಿಸಲಾಗುತ್ತಿದೆ"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"ಮೊಬೈಲ್ ಡೇಟಾ ಯಾವಾಗಲೂ ಸಕ್ರಿಯ"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"ಟೆಥರಿಂಗ್ಗಾಗಿ ಹಾರ್ಡ್ವೇರ್ ವೇಗವರ್ಧನೆ"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"ಹೆಸರುಗಳಿಲ್ಲದ ಬ್ಲೂಟೂತ್ ಸಾಧನಗಳನ್ನು ತೋರಿಸಿ"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"ವೈರ್ಲೆಸ್ ಪ್ರದರ್ಶನ ಪ್ರಮಾಣೀಕರಣಕ್ಕಾಗಿ ಆಯ್ಕೆಗಳನ್ನು ತೋರಿಸು"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi ಲಾಗಿಂಗ್ ಮಟ್ಟನ್ನು ಹೆಚ್ಚಿಸಿ, Wi‑Fi ಆಯ್ಕೆಯಲ್ಲಿ ಪ್ರತಿಯೊಂದು SSID RSSI ತೋರಿಸಿ"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"ಬ್ಯಾಟರಿ ಹೆಚ್ಚು ಬಾಳಿಕೆ ಬರುವಂತೆ ಮಾಡುತ್ತದೆ ಮತ್ತು ನೆಟ್ವರ್ಕ್ ಕಾರ್ಯಕ್ಷಮತೆಯನ್ನು ಸುಧಾರಿಸುತ್ತದೆ"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"ಮೀಟರ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"ಮೀಟರ್ ಮಾಡಲಾಗಿಲ್ಲ"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ಲಾಗರ್ ಬಫರ್ ಗಾತ್ರಗಳು"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 0ac0848..abdfa04 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -156,7 +156,7 @@
<string name="tts_settings_title" msgid="1237820681016639683">"TTS 출력"</string>
<string name="tts_default_rate_title" msgid="6030550998379310088">"말하는 속도"</string>
<string name="tts_default_rate_summary" msgid="4061815292287182801">"텍스트를 읽어주는 속도"</string>
- <string name="tts_default_pitch_title" msgid="6135942113172488671">"피치"</string>
+ <string name="tts_default_pitch_title" msgid="6135942113172488671">"음조"</string>
<string name="tts_default_pitch_summary" msgid="1944885882882650009">"합성 음성의 어조에 영향을 미침"</string>
<string name="tts_default_lang_title" msgid="8018087612299820556">"언어"</string>
<string name="tts_lang_use_system" msgid="2679252467416513208">"시스템 언어 사용"</string>
@@ -200,7 +200,7 @@
<string name="development_settings_not_available" msgid="4308569041701535607">"이 사용자는 개발자 옵션을 사용할 수 없습니다."</string>
<string name="vpn_settings_not_available" msgid="956841430176985598">"이 사용자는 VPN 설정을 수정할 수 없습니다."</string>
<string name="tethering_settings_not_available" msgid="6765770438438291012">"이 사용자는 테더링 설정을 수정할 수 없습니다."</string>
- <string name="apn_settings_not_available" msgid="7873729032165324000">"이 사용자는 액세스포인트 네임(APN) 설정을 수정할 수 없습니다."</string>
+ <string name="apn_settings_not_available" msgid="7873729032165324000">"이 사용자는 액세스 포인트 이름(APN) 설정을 수정할 수 없습니다."</string>
<string name="enable_adb" msgid="7982306934419797485">"USB 디버깅"</string>
<string name="enable_adb_summary" msgid="4881186971746056635">"USB가 연결된 경우 디버그 모드 사용"</string>
<string name="clear_adb_keys" msgid="4038889221503122743">"USB 디버깅 권한 승인 취소"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"네트워크"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"무선 디스플레이 인증서"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi-Fi 상세 로깅 사용"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi 검색 제한"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"항상 모바일 데이터 활성화"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"테더링 하드웨어 가속"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"이름이 없는 블루투스 기기 표시"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"연결할 수 없음"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"무선 디스플레이 인증서 옵션 표시"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi 로깅 수준을 높이고, Wi‑Fi 선택도구에서 SSID RSSI당 값을 표시"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"배터리 소모를 줄이고 네트워크 성능을 개선합니다."</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"종량제 네트워크"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"무제한 네트워크"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"로거 버퍼 크기"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 4991ddb..435ce53 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -142,10 +142,10 @@
<string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Өчүрүлгөн колдонмолор жана колдонуучулар"</string>
<string name="data_usage_ota" msgid="5377889154805560860">"Тутум жаңыртуулары"</string>
<string name="tether_settings_title_usb" msgid="6688416425801386511">"USB модем"</string>
- <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Ташыма кошулуу чекити"</string>
+ <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Wi-Fi байланыш түйүнү"</string>
<string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Bluetooth модем"</string>
<string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Жалгаштыруу"</string>
- <string name="tether_settings_title_all" msgid="8356136101061143841">"Жалгаштыруу жана ташыма чекит"</string>
+ <string name="tether_settings_title_all" msgid="8356136101061143841">"Модем режими"</string>
<string name="managed_user_title" msgid="8109605045406748842">"Жумуш профилинин колднмлр"</string>
<string name="user_guest" msgid="8475274842845401871">"Конок"</string>
<string name="unknown" msgid="1592123443519355854">"Белгисиз"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Тармактар"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Зымсыз мониторлорду тастыктамалоо"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi‑Fi дайын-даректүү журналы"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi тармактарын издөөнү жөнгө салуу"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Мобилдик Интернет иштей берет"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Модем режиминде аппараттын иштешин тездетүү"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Аталышсыз Bluetooth түзмөктөрү көрсөтүлсүн"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Туташпай койду"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Зымсыз мониторлорду тастыктамалоо параметрлери көрүнүп турат"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fi тандалганда ар бир SSID үчүн RSSI көрүнүп турат"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Батареянын коротулушун чектеп, тармактын иштешин жакшыртат"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Трафик ченелет"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Чектелбеген тармак"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Журнал буферинин өлчөмү"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 59ad7cc..ed88bc5 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"ການສ້າງເຄືອຂ່າຍ"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"ສະແດງການຮັບຮອງຂອງລະບົບໄຮ້ສາຍ"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"ເປີດນຳໃຊ້ການເກັບປະຫວັດ Verbose Wi‑Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"ການຈຳກັດການສະແກນ Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"ເປີດໃຊ້ອິນເຕີເນັດມືຖືຕະຫຼອດເວລາ"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"ເປີດໃຊ້ການເລັ່ງຄວາມໄວດ້ວຍຮາດແວ"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"ສະແດງອຸປະກອນ Bluetooth ທີ່ບໍ່ມີຊື່"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"ບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"ສະແດງໂຕເລືອກສຳລັບການສະແດງການຮັບຮອງລະບົບໄຮ້ສາຍ"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"ເພີ່ມລະດັບການເກັບປະຫວັດ Wi‑Fi, ສະແດງຕໍ່ SSID RSSI ໃນ Wi‑Fi Picker"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"ຫຼຸດການໃຊ້ແບັດເຕີຣີ ແລະ ປັບປຸງປະສິດທິພາບເຄືອຂ່າຍ"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"ມີການວັດແທກ"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"ບໍ່ໄດ້ວັດແທກ"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ຂະໜາດບັບເຟີຕົວບັນທຶກ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index bde71e3..c10bd00 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Tinklai"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Belaidžio rodymo sertifikavimas"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Įgal. „Wi‑Fi“ daugiaž. įraš. į žurnalą"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"„Wi‑Fi“ nuskaitymo ribojimas"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobiliojo ryšio duomenys visada suaktyvinti"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Įrenginio kaip modemo naudojimo aparatinės įrangos spartinimas"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Rodyti „Bluetooth“ įrenginius be pavadinimų"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Prisijungti nepavyko"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Rodyti belaidžio rodymo sertifikavimo parinktis"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Padidinti „Wi‑Fi“ įrašymo į žurnalą lygį, rodyti SSID RSSI „Wi-Fi“ rinkiklyje"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Sumažinamas akumuliatoriaus eikvojimas ir patobulinamas tinklo našumas"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Matuojamas"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Neišmatuotas"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Registruotuvo buferio dydžiai"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 5a1805a..8feb5ab 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Tīklošana"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Bezvadu attēlošanas sertifikācija"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Iespējot Wi‑Fi detalizēto reģistrēšanu"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi meklēšanas ierobežošana"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Vienmēr aktīvs mobilo datu savienojums"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Paātrināta aparatūras darbība piesaistei"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Rādīt Bluetooth ierīces bez nosaukumiem"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Nevarēja izveidot savienojumu."</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Rādīt bezvadu attēlošanas sertifikācijas iespējas"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Palieliniet Wi‑Fi reģistrēšanas līmeni; rādīt katram SSID RSSI Wi‑Fi atlasītājā."</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Samazina akumulatora izlādi un uzlabo tīkla veiktspēju"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Maksas"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Bezmaksas"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Reģistrētāja buferu lielumi"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 5be177d..8bed22f 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Вмрежување"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Приказ на сертификација на безжична мрежа"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Овозможи преопширно пријавување Wi‑Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Регулирање на скенирањето за Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Мобилниот интернет е секогаш активен"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Хардверско забрзување за врзување"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Прикажувај уреди со Bluetooth без имиња"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Не може да се поврзе"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Покажи ги опциите за безжичен приказ на сертификат"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Зголеми Wi‑Fi ниво на пријавување, прикажи по SSID RSSI во Wi‑Fi бирач"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Го намалува искористувањето на батеријата и ја подобрува изведбата на мрежата"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Со ограничен интернет"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Без ограничен интернет"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Величини на меѓумеморија за дневникот"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index d3ebd99..7016b7f 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"നെറ്റ്വര്ക്കിംഗ്"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"വയർലെസ് ഡിസ്പ്ലേ സർട്ടിഫിക്കേഷൻ"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"വൈഫൈ വെർബോസ് ലോഗിംഗ് പ്രവർത്തനക്ഷമമാക്കുക"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"വൈഫൈ സ്കാൻ പ്രവർത്തനരഹിതമാക്കുന്നു"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"മൊബൈൽ ഡാറ്റ എല്ലായ്പ്പോഴും സജീവം"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"ടെതറിംഗ് ഹാർഡ്വെയർ ത്വരിതപ്പെടുത്തൽ"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"പേരില്ലാത്ത Bluetooth ഉപകരണങ്ങൾ കാണിക്കുക"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"കണക്റ്റ് ചെയ്യാനായില്ല"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"വയർലെസ് ഡിസ്പ്ലേ സർട്ടിഫിക്കേഷനായി ഓപ്ഷനുകൾ ദൃശ്യമാക്കുക"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"വൈഫൈ പിക്കറിൽ ഓരോ SSID RSSI പ്രകാരം കാണിക്കാൻ വൈഫൈ ലോഗിംഗ് നില വർദ്ധിപ്പിക്കുക"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"ബാറ്ററി ചാർജ് വേഗത്തിൽ തീരുന്ന അവസ്ഥ കുറച്ച് നെറ്റ്വർക്ക് പ്രകടനം മെച്ചപ്പെടുത്തുന്നു"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"മീറ്റർചെയ്ത"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"മീറ്റർമാപകമല്ലാത്തത്"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ലോഗർ ബഫർ വലുപ്പം"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 46029c7..80e78a3 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Сүлжээ"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Утасгүй дэлгэцийн сертификат"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi‑Fi дэлгэрэнгүй лог-г идэвхжүүлэх"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi скан бууруулалт"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Мобайл дата байнга идэвхтэй"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Модем болгох техник хангамжийн хурдасгуур"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Нэргүй Bluetooth төхөөрөмжийг харуулах"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Холбогдож чадсангүй"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Утасгүй дэлгэцийн сертификатын сонголтыг харуулах"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi лог-н түвшинг нэмэгдүүлэх, Wi‑Fi Сонгогч дээрх SSID-д ногдох RSSI-г харуулах"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Батарей зарцуулалтыг бууруулж, сүлжээний гүйцэтгэлийг сайжруулдаг"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Хязгаартай"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Хязгааргүй"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Логгерын буферын хэмжээ"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 5f6acf1..367b13d 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -78,7 +78,7 @@
<string name="bluetooth_active_battery_level_untethered" msgid="6662649951391456747">"अॅक्टिव्ह, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> बॅटरी, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> बॅटरी"</string>
<string name="bluetooth_battery_level" msgid="1447164613319663655">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> बॅटरी"</string>
<string name="bluetooth_battery_level_untethered" msgid="5974406100211667177">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> बॅटरी, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> बॅटरी"</string>
- <string name="bluetooth_active_no_battery_level" msgid="8380223546730241956">"अॅक्टिव्ह"</string>
+ <string name="bluetooth_active_no_battery_level" msgid="8380223546730241956">"अॅक्टिव्ह"</string>
<string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"मीडिया ऑडिओ"</string>
<string name="bluetooth_profile_headset" msgid="7815495680863246034">"फोन कॉल"</string>
<string name="bluetooth_profile_opp" msgid="9168139293654233697">"फाइल स्थानांतरण"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"नेटवर्किंग"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"वायरलेस डिस्प्ले प्रमाणीकरण"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"वाय-फाय व्हर्बोझ लॉगिंग सुरू करा"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"वाय-फाय स्कॅन थ्रॉटलिंग"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"मोबाइल डेटा नेहमी सक्रिय"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"टेदरिंग हार्डवेअर प्रवेग"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"नावांशिवाय ब्लूटूथ डिव्हाइस दाखवा"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"कनेक्ट करू शकलो नाही"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"वायरलेस डिस्प्ले प्रमाणिकरणाचे पर्याय दाखवा"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"वाय-फाय लॉगिंग स्तर वाढवा, वाय-फाय सिलेक्टरमध्ये प्रति SSID RSSI दर्शवा"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"बॅटरी जलदरीतीने संपण्यापासून रोखते आणि नेटवर्क परफॉर्मन्समध्ये सुधारणा करते"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"मीटरने मोजलेले"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"मीटरने न मोजलेले"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"लॉगर बफर आकार"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 03ff0bb..3841a3b 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Perangkaian"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Pensijilan paparan wayarles"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Dayakan Pengelogan Berjela-jela Wi-Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Pendikitan pengimbasan Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Data mudah alih sentiasa aktif"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Pecutan perkakasan penambatan"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Tunjukkan peranti Bluetooth tanpa nama"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Tidak dapat menyambung"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Tunjukkan pilihan untuk pensijilan paparan wayarles"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Tingkatkan tahap pengelogan Wi-Fi, tunjuk setiap SSID RSSI dalam Pemilih Wi-Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Mengurangkan penyusutan bateri & meningkatkan prestasi rangkaian"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Bermeter"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Tidak bermeter"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Saiz penimbal pengelog"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 4500209..38f128a 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -21,7 +21,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ကွန်ယက်များကို စကင်မလုပ်နိုင်ပါ"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"တစ်ခုမျှ မဟုတ်ပါ"</string>
+ <string name="wifi_security_none" msgid="7985461072596594400">"မရှိ"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"သိမ်းဆည်းပြီး"</string>
<string name="wifi_disconnected" msgid="8085419869003922556">"ချိတ်ဆက်မထားပါ"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ပိတ်ထားသည်"</string>
@@ -35,7 +35,7 @@
<string name="wifi_not_in_range" msgid="1136191511238508967">"စက်ကွင်းထဲတွင် မဟုတ်ပါ"</string>
<string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"အလိုအလျောက်ချိတ်ဆက်မည်မဟုတ်ပါ"</string>
<string name="wifi_no_internet" msgid="4663834955626848401">"အင်တာနက် ချိတ်ဆက်မှု မရှိပါ"</string>
- <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> မှသိမ်းဆည်းခဲ့သည်"</string>
+ <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> က သိမ်းဆည်းခဲ့သည်"</string>
<string name="connected_via_network_scorer" msgid="5713793306870815341">"%1$s မှတစ်ဆင့် အလိုအလျောက် ချိတ်ဆက်ထားပါသည်"</string>
<string name="connected_via_network_scorer_default" msgid="7867260222020343104">"ကွန်ရက်အဆင့်သတ်မှတ်ပေးသူ မှတစ်ဆင့် အလိုအလျောက် ချိတ်ဆက်ထားပါသည်"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string>
@@ -148,12 +148,12 @@
<string name="tether_settings_title_all" msgid="8356136101061143841">"တဆင့်ချိတ်ဆက်ခြင်း၊ ဟော့စပေါ့"</string>
<string name="managed_user_title" msgid="8109605045406748842">"အလုပ်သုံးအက်ပ်များအားလုံး"</string>
<string name="user_guest" msgid="8475274842845401871">"ဧည့်သည်"</string>
- <string name="unknown" msgid="1592123443519355854">"မသိပါ"</string>
+ <string name="unknown" msgid="1592123443519355854">"မသိ"</string>
<string name="running_process_item_user_label" msgid="3129887865552025943">"အသုံးပြုသူ- <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="313159469856372621">"မူရင်းအချို့ သတ်မှတ်ပြီး"</string>
<string name="launch_defaults_none" msgid="4241129108140034876">"ပုံမှန်သတ်မှတ်ထားခြင်းမရှိ"</string>
<string name="tts_settings" msgid="8186971894801348327">"စာသားမှစကားပြောပြောင်း ဆက်တင်များ"</string>
- <string name="tts_settings_title" msgid="1237820681016639683">"စာသားမှ အသံထွက်စေခြင်း"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"စာသားမှ စကားပြောသို့ အထွက်"</string>
<string name="tts_default_rate_title" msgid="6030550998379310088">"စကားပြောနှုန်း"</string>
<string name="tts_default_rate_summary" msgid="4061815292287182801">"စာတမ်းအားပြောဆိုသော အမြန်နှုန်း"</string>
<string name="tts_default_pitch_title" msgid="6135942113172488671">"အသံအနိမ့်အမြင့်"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"ချိတ်ဆက်ဆောင်ရွက်ခြင်း"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"ကြိုးမဲ့ပြသမှု အသိအမှတ်ပြုလက်မှတ်"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi‑Fi Verbose မှတ်တမ်းတင်ခြင်းအား ဖွင့်မည်"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi ရှာဖွေခြင်း ထိန်းချုပ်မှု"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"မိုဘိုင်းဒေတာကို အမြဲဖွင့်ထားရန်"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"ဖုန်းကို မိုဒမ်အဖြစ်အသုံးပြုမှု စက်ပစ္စည်းဖြင့် အရှိန်မြှင့်တင်ခြင်း"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"အမည်မရှိသော ဘလူးတုသ်စက်ပစ္စည်းများကို ပြသရန်"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"ချိတ်ဆက်၍ မရပါ"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"ကြိုးမဲ့ အခင်းအကျင်း အသိအမှတ်ပြုလက်မှတ်အတွက် ရွေးချယ်စရာများပြရန်"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi မှတ်တမ်းတင်ခြင်း နှုန်းအားမြင့်ကာ၊ Wi‑Fi ရွေးရာတွင် SSID RSSI ဖြင့်ပြပါ"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"ဘက်ထရီ အသုံးပြုမှုကို လျှော့ကျစေပြီး ကွန်ရက်စွမ်းဆောင်ရည်ကို ပိုမိုကောင်းမွန်စေသည်"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"အခမဲ့ မဟုတ်ပါ"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"အခမဲ့"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"မှတ်တမ်းကြားခံနယ် အရွယ်အစားများ"</string>
@@ -402,7 +404,7 @@
<string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"အားပြည့်ရန် <xliff:g id="TIME">%1$s</xliff:g> လိုပါသည်"</string>
<string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> − အားပြည့်ရန် <xliff:g id="TIME">%2$s</xliff:g> ကျန်သည်"</string>
- <string name="battery_info_status_unknown" msgid="196130600938058547">"မသိပါ"</string>
+ <string name="battery_info_status_unknown" msgid="196130600938058547">"မသိ"</string>
<string name="battery_info_status_charging" msgid="1705179948350365604">"အားသွင်းနေပါသည်"</string>
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"အားသွင်းနေပါသည်"</string>
<string name="battery_info_status_discharging" msgid="310932812698268588">"အားသွင်းမနေပါ"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index abb3410..144d4c2 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Nettverk"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Trådløs skjermsertifisering"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Slå på detaljert Wi-Fi-loggføring"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Begrensning av Wi‑Fi-skanning"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobildata er alltid aktiv"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Maskinvareakselerasjon for internettdeling"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Vis Bluetooth-enheter uten navn"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Kunne ikke koble til"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Vis alternativer for sertifisering av trådløs skjerm"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Øk Wi-Fi-loggenivå – vis per SSID RSSI i Wi-Fi-velgeren"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Reduserer batteriforbruket og forbedrer nettverksytelsen"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Med datamåling"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Uten datamåling"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Bufferstørrelser for logg"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 7d71fdc..fd45cb0 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"नेटवर्किङ"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"ताररहित प्रदर्शन प्रमाणीकरण"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi-Fi वर्बोज लग सक्षम पार्नुहोस्"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi स्क्यान थ्रोटलिङ"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"मोबाइल डेटा सधैँ सक्रिय राख्नुहोस्"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"टेदरिङको लागि हार्डवेयरको प्रवेग"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"नामकरण नगरिएका ब्लुटुथ यन्त्रहरू देखाउनुहोस्"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"जडान गर्न सकिएन"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"ताररहित प्रदर्शन प्रमाणीकरणका लागि विकल्पहरू देखाउनुहोस्"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fi लग स्तर बढाउनुहोस्, Wi-Fi चयनकर्तामा प्रति SSID RSSI देखाइन्छ"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"ब्याट्रीको खपत कम गरी नेटवर्कको कार्यसम्पादनमा सुधार गर्दछ"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"मिटर गरिएको जडान भनी चिन्ह लगाइएको"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"मिटर नगरिएको"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"लगर बफर आकारहरू"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index d8154ac..c50b43a 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -37,7 +37,7 @@
<string name="wifi_no_internet" msgid="4663834955626848401">"Geen internettoegang"</string>
<string name="saved_network" msgid="4352716707126620811">"Opgeslagen door \'<xliff:g id="NAME">%1$s</xliff:g>\'"</string>
<string name="connected_via_network_scorer" msgid="5713793306870815341">"Automatisch verbonden via %1$s"</string>
- <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"Automatisch verbonden via provider van netwerkbeoordelingen"</string>
+ <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"Automatisch verbonden via netwerkbeoordelaar"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Verbonden via %1$s"</string>
<string name="connected_via_app" msgid="5571999941988929520">"Verbonden via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1617440946846329613">"Beschikbaar via %1$s"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Netwerken"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certificering van draadloze weergave"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Uitgebreide wifi-logregistratie insch."</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wifi-scannen beperken"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobiele data altijd actief"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardwareversnelling voor tethering"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Bluetooth-apparaten zonder namen weergeven"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Kan geen verbinding maken"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Opties weergeven voor certificering van draadloze weergave"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Logniveau voor wifi verhogen, weergeven per SSID RSSI in wifi-kiezer"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Verlaagt het batterijverbruik en verbetert de netwerkprestaties"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Met datalimiet"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Gratis"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Logger-buffergrootten"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 0ca4e3e..648cc4a 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"ନେଟ୍ୱର୍କିଙ୍ଗ"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"ୱାୟରଲେସ୍ ଡିସ୍ପ୍ଲେ ସାର୍ଟିଫିକେସନ୍"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"ୱାଇ-ଫାଇ ଭର୍ବୋସ୍ ଲଗିଙ୍ଗ ସକ୍ଷମ କରନ୍ତୁ"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"ୱାଇ-ଫାଇ ସ୍କାନ୍ ନିୟନ୍ତ୍ରଣ"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"ମୋବାଇଲ୍ ଡାଟା ସର୍ବଦା ସକ୍ରିୟ"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"ଟିଥରିଙ୍ଗ ହାର୍ଡୱେର ଆକ୍ସିଲିରେସନ୍"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"ବ୍ଲୁଟୂଥ୍ ଡିଭାଇସ୍ଗୁଡ଼ିକୁ ନାମ ବିନା ଦେଖନ୍ତୁ"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"କନେକ୍ଟ କରିହେଲା ନାହିଁ"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"ୱେୟାରଲେସ୍ ଡିସ୍ପ୍ଲେ ସାର୍ଟିଫିକେସନ୍ ପାଇଁ ବିକଳ୍ପ ଦେଖାନ୍ତୁ"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"ୱାଇ-ଫାଇ ଲଗିଙ୍ଗ ସ୍ତର ବଢ଼ାନ୍ତୁ, ୱାଇ-ଫାଇ ପିକର୍ରେ ପ୍ରତି SSID RSSI ଦେଖାନ୍ତୁ"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"ବ୍ୟାଟେରୀ ଖର୍ଚ୍ଚ କମ୍ ଏବଂ ନେଟ୍ୱାର୍କ ପ୍ରଦର୍ଶନ ଉନ୍ନତ କରିଥାଏ"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"ମପାଯାଉଥିବା"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"ମପାଯାଉନଥିବା"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ଲଗର୍ ବଫର୍ ସାଇଜ୍"</string>
@@ -460,7 +462,7 @@
<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>
- <string name="zen_mode_forever" msgid="2704305038191592967">"ଆପଣ ବନ୍ଦ ନକରିବା ପର୍ଯ୍ୟନ୍ତ DND ଅନ୍ ରହିବ"</string>
+ <string name="zen_mode_forever" msgid="2704305038191592967">"ଆପଣ ବନ୍ଦ ନକରିବା ପର୍ଯ୍ୟନ୍ତ"</string>
<string name="time_unit_just_now" msgid="6363336622778342422">"ଏହିକ୍ଷଣି"</string>
<string name="media_transfer_this_device_name" msgid="1636276898262571213">"ଏହି ଡିଭାଇସ୍"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 6fff4d8..7967c71 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"ਨੈੱਟਵਰਕਿੰਗ"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"ਵਾਇਰਲੈੱਸ ਡਿਸਪਲੇ ਪ੍ਰਮਾਣੀਕਰਨ"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"ਵਾਈ-ਫਾਈ ਵਰਬੋਸ ਲੌਗਿੰਗ ਚਾਲੂ ਕਰੋ"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"ਸੀਮਤ ਵਾਈ‑ਫਾਈ ਸਕੈਨ"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"ਮੋਬਾਈਲ ਡਾਟਾ ਹਮੇਸ਼ਾਂ ਕਿਰਿਆਸ਼ੀਲ"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"ਟੈਦਰਿੰਗ ਹਾਰਡਵੇਅਰ ਐਕਸੈੱਲਰੇਸ਼ਨ"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"ਅਨਾਮ ਬਲੂਟੁੱਥ ਡੀਵਾਈਸਾਂ ਦਿਖਾਓ"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"ਵਾਇਰਲੈੱਸ ਡਿਸਪਲੇ ਪ੍ਰਮਾਣੀਕਰਨ ਲਈ ਚੋਣਾਂ ਪ੍ਰਦਰਸ਼ਿਤ ਕਰੋ"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"ਵਾਈ‑ਫਾਈ ਲੌਗਿੰਗ ਪੱਧਰ ਵਧਾਓ, ਵਾਈ‑ਫਾਈ Picker ਵਿੱਚ ਪ੍ਰਤੀ SSID RSSI ਦਿਖਾਓ"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"ਬੈਟਰੀ ਦੀ ਵਰਤੋਂ ਘਟਾ ਕੇ ਨੈੱਟਵਰਕ ਕਾਰਗੁਜ਼ਾਰੀ ਨੂੰ ਬਿਹਤਰ ਬਣਾਉਂਦਾ ਹੈ"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"ਮੀਟਰਬੱਧ ਕੀਤਾ ਗਿਆ"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"ਗੈਰ-ਮੀਟਰਬੱਧ ਕੀਤਾ ਗਿਆ"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ਲੌਗਰ ਬਫ਼ਰ ਆਕਾਰ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index bed4d63..60a4c53 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Sieci"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Wyświetlacz bezprzewodowy"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Szczegółowy dziennik Wi-Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Ograniczanie skanowania Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobilna transmisja danych zawsze aktywna"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Akceleracja sprzętowa tetheringu"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Pokaż urządzenia Bluetooth bez nazw"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Nie udało się połączyć"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Pokaż opcje certyfikacji wyświetlacza bezprzewodowego"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Zwiększ poziom rejestrowania Wi‑Fi, pokazuj według RSSI SSID w selektorze Wi‑Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Zmniejsza zużycie baterii i zwiększa wydajność sieci"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Użycie danych jest mierzone"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Użycie danych nie jest mierzone"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Rozmiary bufora rejestratora"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 1348715..dd0a75b 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -23,7 +23,7 @@
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar a existência de redes"</string>
<string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Salva"</string>
- <string name="wifi_disconnected" msgid="8085419869003922556">"Desconectado"</string>
+ <string name="wifi_disconnected" msgid="8085419869003922556">"Desconectada"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="5168315140978066096">"Não conectado devido à baixa qualidade da rede"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Redes"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certificação de Display sem fio"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Ativar registro detalhado de Wi-Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Limitar busca por Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Dados móveis sempre ativos"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Aceleração de hardware de tethering"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Mostrar dispositivos Bluetooth sem nomes"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Não foi possível conectar"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opções de certificação de Display sem fio"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar o nível de registro de Wi-Fi; mostrar conforme o RSSI do SSID no seletor de Wi-Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Reduz o consumo de bateria e melhora o desempenho da rede"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Limitada"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Ilimitada"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Tamanhos de buffer de logger"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 3b0b86c..501cf50 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Redes"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certificação de display sem fios"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Ativar o registo verboso de Wi-Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Controlo da procura de Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Dados móveis sempre ativos"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Aceleração de hardware para ligação (à Internet) via telemóvel"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Mostrar dispositivos Bluetooth sem nomes"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Não foi possível estabelecer ligação"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opções da certificação de display sem fios"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar o nível de reg. de Wi-Fi, mostrar por RSSI de SSID no Selec. de Wi-Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Reduz o consumo rápido da bateria e melhora o desempenho da rede"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Acesso limitado"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Acesso ilimitado"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Tamanhos da memória intermédia do registo"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 1348715..dd0a75b 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -23,7 +23,7 @@
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar a existência de redes"</string>
<string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Salva"</string>
- <string name="wifi_disconnected" msgid="8085419869003922556">"Desconectado"</string>
+ <string name="wifi_disconnected" msgid="8085419869003922556">"Desconectada"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="5168315140978066096">"Não conectado devido à baixa qualidade da rede"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Redes"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certificação de Display sem fio"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Ativar registro detalhado de Wi-Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Limitar busca por Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Dados móveis sempre ativos"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Aceleração de hardware de tethering"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Mostrar dispositivos Bluetooth sem nomes"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Não foi possível conectar"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opções de certificação de Display sem fio"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar o nível de registro de Wi-Fi; mostrar conforme o RSSI do SSID no seletor de Wi-Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Reduz o consumo de bateria e melhora o desempenho da rede"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Limitada"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Ilimitada"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Tamanhos de buffer de logger"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 1cbd70bf..fb020e2 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -83,7 +83,7 @@
<string name="bluetooth_profile_headset" msgid="7815495680863246034">"Apeluri telefonice"</string>
<string name="bluetooth_profile_opp" msgid="9168139293654233697">"Transfer de fișiere"</string>
<string name="bluetooth_profile_hid" msgid="3680729023366986480">"Dispozitiv de intrare"</string>
- <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Acces internet"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Acces la internet"</string>
<string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Acces la Agendă"</string>
<string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Utilizați pentru a permite accesul la Agendă"</string>
<string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Distribuirea conexiunii la internet"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Conectare la rețele"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certificare Ecran wireless"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Înregistrare prin Wi-Fi de volume mari de date"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Limitare căutare de rețele Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Date mobile permanent active"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Accelerare hardware pentru tethering"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Afișați dispozitivele Bluetooth fără nume"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Nu s-a putut conecta"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Afișați opțiunile pentru certificarea Ecran wireless"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Măriți niv. de înr. prin Wi‑Fi, afișați în fcț. de SSID RSSI în Selectorul Wi‑Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Reduce descărcarea bateriei și îmbunătățește performanța rețelei"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Contorizată"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Necontorizată"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Dimensiunile memoriei temporare a jurnalului"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 657e6a9..db43c75 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Сети"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Серт. беспроводн. мониторов"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Подробный журнал Wi‑Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Регулирование поиска сетей Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Не отключать мобильный Интернет"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Аппаратное ускорение в режиме модема"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Показывать Bluetooth-устройства без названий"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Ошибка подключения"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Показывать параметры сертификации беспроводных мониторов"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Вести подробный журнал, показывать RSSI для каждого SSID при выборе сети"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Уменьшает расход заряда батареи и улучшает работу сетей."</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Сеть с тарификацией трафика"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Сеть без тарификации трафика"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Размер буфера журнала"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index f499253..925d5b4 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"ජාලකරණය"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"නොරැහැන් සංදර්ශක සහතිකය"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"විස්තරාත්මක Wi‑Fi ලොග් කිරීම සබල කරන්න"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi ස්කෑන් අවකරණය"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"ජංගම දත්ත සැමවිට ක්රියාකාරීය"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"ටෙදරින් දෘඪාංග ත්වරණය"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"නම් නොමැති බ්ලූටූත් උපාංග පෙන්වන්න"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"සම්බන්ධ වීමට නොහැකි විය"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"නොරැහැන් සංදර්ශක සහතිකය සඳහා විකල්ප පෙන්වන්න"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi ලොග් මට්ටම වැඩි කරන්න, Wi‑Fi තෝරනයෙහි SSID RSSI අනුව පෙන්වන්න"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"බැටරි බැසීම අඩු කරන අතර ජාල කාර්ය සාධනය වැඩි දියුණු කරයි"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"මනිනු ලැබේ"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"මනින්නේ නැත"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ලෝගයේ අන්තරාවක ප්රමාණය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index a7895c4..e28825c 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -91,8 +91,8 @@
<string name="bluetooth_profile_sap" msgid="5764222021851283125">"Prístup k SIM karte"</string>
<string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD zvuk: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD zvuk"</string>
- <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Načúvacie pomôcky"</string>
- <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Pripojené k načúvacím pomôckam"</string>
+ <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Načúvadlá"</string>
+ <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="3051944447369418317">"Pripojené k načúvadlám"</string>
<string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Pripojené ku zvukovému médiu"</string>
<string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Pripojené ku zvuku telefónu"</string>
<string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Pripojené na server pre prenos údajov"</string>
@@ -109,7 +109,7 @@
<string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Použiť pre zvuk telefónu"</string>
<string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Použiť na prenos súborov"</string>
<string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Použiť pre vstup"</string>
- <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Použiť pre načúvacie pomôcky"</string>
+ <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="8843499209204010224">"Použiť pre načúvadlá"</string>
<string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Párovať"</string>
<string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"PÁROVAŤ"</string>
<string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Zrušiť"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Siete"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certifikácia bezdrôtového zobrazenia"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Podrobné denníky Wi‑Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Pribrzdenie vyhľadávania sietí Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobilné dáta ponechať vždy aktívne"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardvérová akcelerácia tetheringu"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Zobrazovať zariadenia Bluetooth bez názvov"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Nepodarilo sa pripojiť"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Zobraziť možnosti certifikácie bezdrôtového zobrazenia"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Zvýšiť úroveň denníkov Wi‑Fi, zobrazovať podľa SSID RSSI pri výbere siete Wi‑Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Znižuje používanie batérie a zlepšuje výkon siete"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Merané"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Bez merania dát"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Vyrovnávacia pamäť nástroja denníkov"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index d21e3ad..213b285 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -219,7 +219,8 @@
<string name="mock_location_app_set" msgid="8966420655295102685">"Aplikacija za simulirano lokacijo: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="debug_networking_category" msgid="7044075693643009662">"Omrežja"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Potrdilo brezžičnega zaslona"</string>
- <string name="wifi_verbose_logging" msgid="4203729756047242344">"Omogoči podrob. zapis. dnevnika za Wi-Fi"</string>
+ <string name="wifi_verbose_logging" msgid="4203729756047242344">"Omogoči podrobno zapisovanje dnevnika za Wi-Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Omejevanje iskanja omrežij Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Prenos podatkov v mobilnem omrežju je vedno aktiven"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Strojno pospeševanje za internetno povezavo prek mobilnega telefona"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Prikaži naprave Bluetooth brez imen"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Povezave ni bilo mogoče vzpostaviti"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Pokaži možnosti za potrdilo brezžičnega zaslona"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Povečaj raven zapis. dnev. za Wi-Fi; v izbir. Wi‑Fi-ja pokaži glede na SSID RSSI"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Zmanjša porabo energije akumulatorja in izboljša delovanje omrežja"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Omejen prenos podatkov"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Z neomejenim prenosom podatkov"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Velikosti medpomnilnikov zapisovalnika dnevnika"</string>
@@ -297,7 +299,7 @@
<string name="show_screen_updates_summary" msgid="2569622766672785529">"Ob posodobitvi osveži celotne površine oken"</string>
<string name="show_hw_screen_updates" msgid="4117270979975470789">"Prikaži posodob. pogleda"</string>
<string name="show_hw_screen_updates_summary" msgid="6506943466625875655">"Osveži poglede v oknih pri risanju"</string>
- <string name="show_hw_layers_updates" msgid="5645728765605699821">"Pokaži pos. sl. str. opr."</string>
+ <string name="show_hw_layers_updates" msgid="5645728765605699821">"Pokaži posodobitve slojev strojne opreme"</string>
<string name="show_hw_layers_updates_summary" msgid="5296917233236661465">"Obarvaj sloje strojne opreme zeleno ob posodobitvi"</string>
<string name="debug_hw_overdraw" msgid="2968692419951565417">"Prekoračitev območja GPE"</string>
<string name="disable_overlays" msgid="2074488440505934665">"Onem. strojni medp."</string>
@@ -315,7 +317,7 @@
<string name="show_non_rect_clip" msgid="505954950474595172">"Odpravljanje težav s postopki nepravokotnega izrezovanja"</string>
<string name="track_frame_time" msgid="6094365083096851167">"Upodob. profilov s HWUI"</string>
<string name="enable_gpu_debug_layers" msgid="3848838293793255097">"Omog. sloje odpr. nap. GPE"</string>
- <string name="enable_gpu_debug_layers_summary" msgid="8009136940671194940">"Apl. za odpr. nap. dovoli nal. sloj. odpr. nap. GPE"</string>
+ <string name="enable_gpu_debug_layers_summary" msgid="8009136940671194940">"Aplikacijam za odpravljanje napak dovoli nalaganje slojev za odpravljanje napak GPE"</string>
<string name="window_animation_scale_title" msgid="6162587588166114700">"Merilo animacije okna"</string>
<string name="transition_animation_scale_title" msgid="387527540523595875">"Merilo animacije prehoda"</string>
<string name="animator_duration_scale_title" msgid="3406722410819934083">"Merilo trajanja animacije"</string>
@@ -334,7 +336,7 @@
<string name="force_resizable_activities_summary" msgid="6667493494706124459">"Poskrbi, da je ne glede na vrednosti v manifestu mogoče vsem aktivnostim spremeniti velikost za način z več okni."</string>
<string name="enable_freeform_support" msgid="1461893351278940416">"Omogočanje oken svobodne oblike"</string>
<string name="enable_freeform_support_summary" msgid="8247310463288834487">"Omogočanje podpore za poskusna okna svobodne oblike"</string>
- <string name="local_backup_password_title" msgid="3860471654439418822">"Geslo za varn. kop. namizja"</string>
+ <string name="local_backup_password_title" msgid="3860471654439418822">"Geslo za varnostno kopijo namizja"</string>
<string name="local_backup_password_summary_none" msgid="6951095485537767956">"Popolne varnostne kopije namizja trenutno niso zaščitene"</string>
<string name="local_backup_password_summary_change" msgid="5376206246809190364">"Dotaknite se, če želite spremeniti ali odstraniti geslo za popolno varnostno kopiranje namizja"</string>
<string name="local_backup_password_toast_success" msgid="582016086228434290">"Novo geslo je nastavljeno"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index b5f93e4..4b38383 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Rrjetet"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certifikimi i ekranit valor"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Aktivizo hyrjen Wi-Fi Verbose"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Përshpejtimi i skanimit të Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Të dhënat celulare gjithmonë aktive"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Përshpejtimi i harduerit për ndarjen e lidhjes (internet)"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Shfaq pajisjet me Bluetooth pa emra"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Nuk mund të lidhej"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Shfaq opsionet për certifikimin e ekranit valor"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Rrit nivelin regjistrues të Wi‑Fi duke shfaqur SSID RSSI-në te Zgjedhësi i Wi‑Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Zvogëlon shkarkimin e baterisë dhe përmirëson cilësinë e funksionimit të rrjetit"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Me matje"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Pa matje"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Madhësitë e regjistruesit"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 2a6e328..0edc985 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Умрежавање"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Сертификација бежичног екрана"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Омогући детаљнију евиденцију за Wi‑Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Успоравање Wi-Fi скенирања"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Мобилни подаци су увек активни"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Хардверско убрзање привезивања"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Прикажи Bluetooth уређаје без назива"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Повезивање није успело"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Приказ опција за сертификацију бежичног екрана"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Повећава ниво евидентирања за Wi‑Fi. Приказ по SSID RSSI-у у бирачу Wi‑Fi мреже"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Смањује потрошњу батерије и побољшава учинак мреже"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Са ограничењем"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Без ограничења"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Величине бафера података у програму за евидентирање"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 9778516..862fc6c 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Nätverk"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certifiering för Wi-Fi-skärmdelning"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Aktivera utförlig loggning för Wi-Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Begränsning av Wi-Fi-sökning"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobildata alltid aktiverad"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Maskinvaruacceleration för internetdelning"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Visa namnlösa Bluetooth-enheter"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Kan inte ansluta"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Visa certifieringsalternativ för Wi-Fi-skärmdelning"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Öka loggningsnivån för Wi-Fi, visa per SSID RSSI i Wi‑Fi Picker"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Sänker batteriförbrukningen och förbättrar nätverksprestandan"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Med datapriser"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Utan datapriser"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Buffertstorlekar för logg"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 0b486f7..12922ca 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -78,12 +78,12 @@
<string name="bluetooth_active_battery_level_untethered" msgid="6662649951391456747">"Inatumika, L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ya betri, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ya betri"</string>
<string name="bluetooth_battery_level" msgid="1447164613319663655">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_battery_level_untethered" msgid="5974406100211667177">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ya betri, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ya betri"</string>
- <string name="bluetooth_active_no_battery_level" msgid="8380223546730241956">"Unaendelea"</string>
+ <string name="bluetooth_active_no_battery_level" msgid="8380223546730241956">"Kimeunganishwa"</string>
<string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Media ya sauti"</string>
<string name="bluetooth_profile_headset" msgid="7815495680863246034">"Simu"</string>
<string name="bluetooth_profile_opp" msgid="9168139293654233697">"Uhamishaji wa faili"</string>
<string name="bluetooth_profile_hid" msgid="3680729023366986480">"Kifaa cha kuingiza"</string>
- <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Ufikivu wa mtandao"</string>
+ <string name="bluetooth_profile_pan" msgid="3391606497945147673">"Ufikiaji wa mtandao"</string>
<string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Kushiriki anwani"</string>
<string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Tumia kwa kushiriki anwani"</string>
<string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Kushiriki muunganisho wa tovuti"</string>
@@ -137,7 +137,7 @@
<string name="accessibility_wifi_signal_full" msgid="7061045677694702">"Nguvu kamili ya mtandao wa Wifi."</string>
<string name="accessibility_wifi_security_type_none" msgid="1223747559986205423">"Mtandao unaotumiwa na mtu yeyote"</string>
<string name="accessibility_wifi_security_type_secured" msgid="862921720418885331">"Mtandao salama"</string>
- <string name="process_kernel_label" msgid="3916858646836739323">"OS ya Android"</string>
+ <string name="process_kernel_label" msgid="3916858646836739323">"Mfumo wa Uendeshaji wa Android"</string>
<string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Programu zilizoondolewa"</string>
<string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Watumiaji na programu ziilizoondolewa"</string>
<string name="data_usage_ota" msgid="5377889154805560860">"Masasisho ya mfumo"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Mtandao"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Chaguo za cheti cha kuonyesha pasiwaya"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Washa Uwekaji kumbukumbu za WiFi kutumia Sauti"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Kudhibiti utafutaji wa Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Iendelee kutumia data ya simu"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Kuongeza kasi kwa kutumia maunzi ili kusambaza mtandao"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Onyesha vifaa vya Bluetooth visivyo na majina"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Imeshindwa kuunganisha"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Onyesha chaguo za cheti cha kuonyesha pasiwaya"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Ongeza hatua ya uwekaji kumbukumbu ya Wi-Fi, onyesha kwa kila SSID RSSI kwenye Kichukuzi cha Wi-Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Hupunguza matumizi ya chaji ya betri na kuboresha utendaji wa mtandao"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Mtandao unapima data"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Mtandao usiopima data"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Ukubwa wa kiweka bafa ya kumbukumbu"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 13741e0..cf442b7 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -23,8 +23,7 @@
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"நெட்வொர்க்குகளுக்கு ஸ்கேன் செய்யப்படவில்லை"</string>
<string name="wifi_security_none" msgid="7985461072596594400">"ஏதுமில்லை"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"சேமிக்கப்பட்டது"</string>
- <!-- no translation found for wifi_disconnected (8085419869003922556) -->
- <skip />
+ <string name="wifi_disconnected" msgid="8085419869003922556">"தொடர்பு துண்டிக்கப்பட்டது"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"முடக்கப்பட்டது"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP உள்ளமைவில் தோல்வி"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="5168315140978066096">"தரம் குறைவான நெட்வொர்க்கின் காரணமாக, இணைக்கப்படவில்லை"</string>
@@ -197,7 +196,7 @@
<string name="category_work" msgid="8699184680584175622">"பணியிடம்"</string>
<string name="development_settings_title" msgid="215179176067683667">"டெவெலப்பர் விருப்பங்கள்"</string>
<string name="development_settings_enable" msgid="542530994778109538">"டெவெலப்பர் விருப்பங்களை இயக்கு"</string>
- <string name="development_settings_summary" msgid="1815795401632854041">"பயன்பாட்டின் மேம்பாட்டிற்காக விருப்பங்களை அமை"</string>
+ <string name="development_settings_summary" msgid="1815795401632854041">"ஆப்ஸின் மேம்பாட்டிற்காக விருப்பங்களை அமை"</string>
<string name="development_settings_not_available" msgid="4308569041701535607">"இவருக்கு, டெவெலப்பர் விருப்பங்கள் இல்லை"</string>
<string name="vpn_settings_not_available" msgid="956841430176985598">"இவரால் VPN அமைப்புகளை மாற்ற முடியாது"</string>
<string name="tethering_settings_not_available" msgid="6765770438438291012">"இவரால் இணைப்புமுறை அமைப்புகளை மாற்ற முடியாது"</string>
@@ -221,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"நெட்வொர்க்கிங்"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"வயர்லெஸ் காட்சிக்கான சான்றிதழ்"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"வைஃபை அதிவிவர நுழைவை இயக்கு"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"வைஃபை ஸ்கேனிங்கை வரம்பிடுதல்"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"மொபைல் டேட்டாவை எப்போதும் இயக்கத்திலேயே வை"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"வன்பொருள் விரைவுப்படுத்துதல் இணைப்பு முறை"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"பெயர்கள் இல்லாத புளூடூத் சாதனங்களைக் காட்டு"</string>
@@ -247,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"இணைக்க முடியவில்லை"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"வயர்லெஸ் காட்சி சான்றுக்கான விருப்பங்களைக் காட்டு"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"வைஃபை நுழைவு அளவை அதிகரித்து, வைஃபை தேர்வியில் ஒவ்வொன்றிற்கும் SSID RSSI ஐ காட்டுக"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"பேட்டரி தீர்ந்துபோவதைக் குறைத்து நெட்வொர்க்கின் செயல்திறனை மேம்படுத்தும்"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"கட்டண நெட்வொர்க்"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"கட்டணமில்லா நெட்வொர்க்"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"லாகர் பஃபர் அளவுகள்"</string>
@@ -355,7 +356,7 @@
<string name="inactive_apps_title" msgid="9042996804461901648">"காத்திருப்பில் உள்ள ஆப்ஸ்"</string>
<string name="inactive_app_inactive_summary" msgid="5091363706699855725">"செயலில் இல்லை. மாற்ற, தட்டவும்."</string>
<string name="inactive_app_active_summary" msgid="4174921824958516106">"செயலில் உள்ளது. மாற்ற, தட்டவும்."</string>
- <string name="standby_bucket_summary" msgid="6567835350910684727">"காத்திருப்பில் உள்ள பயன்பாட்டின் நிலை:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
+ <string name="standby_bucket_summary" msgid="6567835350910684727">"காத்திருப்பில் உள்ள ஆப்ஸின் நிலை:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
<string name="runningservices_settings_title" msgid="8097287939865165213">"இயங்கும் சேவைகள்"</string>
<string name="runningservices_settings_summary" msgid="854608995821032748">"தற்போது இயக்கத்தில் இருக்கும் சேவைகளைப் பார்த்து கட்டுப்படுத்து"</string>
<string name="select_webview_provider_title" msgid="4628592979751918907">"WebView செயல்படுத்தல்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 99affe4..83636dc 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"నెట్వర్కింగ్"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"వైర్లెస్ ప్రదర్శన ప్రామాణీకరణ"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi‑Fi విశదీకృత లాగింగ్ను ప్రారంభించండి"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi స్కాన్ కుదింపు"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"మొబైల్ డేటాని ఎల్లప్పుడూ యాక్టివ్గా ఉంచు"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"టెథెరింగ్ హార్డ్వేర్ వేగవృద్ధి"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"పేర్లు లేని బ్లూటూత్ పరికరాలు చూపించు"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"కనెక్ట్ చేయడం సాధ్యపడలేదు"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"వైర్లెస్ ప్రదర్శన సర్టిఫికెట్ కోసం ఎంపికలను చూపు"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi ఎంపికలో SSID RSSI ప్రకారం చూపబడే Wi‑Fi లాగింగ్ స్థాయిని పెంచండి"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"బ్యాటరీ శక్తి వినియోగాన్ని తగ్గించి & నెట్వర్క్ పనితీరును మెరుగుపరుస్తుంది"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"గణించబడుతోంది"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"గణించబడటం లేదు"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"లాగర్ బఫర్ పరిమాణాలు"</string>
@@ -451,7 +453,7 @@
<string name="okay" msgid="1997666393121016642">"సరే"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"ఆన్ చేయండి"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"అంతరాయం కలిగించవద్దును ఆన్ చేయండి"</string>
- <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"ఎప్పటికీ"</string>
+ <string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"ఎప్పటికీ వద్దు"</string>
<string name="zen_interruption_level_priority" msgid="2078370238113347720">"ప్రాధాన్యత మాత్రమే"</string>
<string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="zen_alarm_warning_indef" msgid="3007988140196673193">"మీరు <xliff:g id="WHEN">%1$s</xliff:g> సెట్ చేసిన మీ తర్వాత అలారం మీరు ఆ లోపల దీన్ని ఆఫ్ చేయకుంటే వినిపించదు"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 4f8b9e4..910b94e 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"เครือข่าย"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"การรับรองการแสดงผลแบบไร้สาย"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"เปิดใช้การบันทึกรายละเอียด Wi-Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"การควบคุมการสแกนหา Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"เปิดใช้เน็ตมือถือเสมอ"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"การเร่งฮาร์ดแวร์การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"แสดงอุปกรณ์บลูทูธที่ไม่มีชื่อ"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"เชื่อมต่อไม่ได้"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"แสดงตัวเลือกสำหรับการรับรองการแสดงผล แบบไร้สาย"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"เพิ่มระดับการบันทึก Wi‑Fi แสดงต่อ SSID RSSI ในตัวเลือก Wi‑Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"ลดการหมดเปลืองแบตเตอรี่และเพิ่มประสิทธิภาพเครือข่าย"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"มีการวัดปริมาณอินเทอร์เน็ต"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"ไม่มีการวัดปริมาณอินเทอร์เน็ต"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"ขนาดบัฟเฟอร์ของตัวบันทึก"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index bc33509..85b6438 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Networking"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Certification ng wireless display"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"I-enable ang Pagla-log sa Wi‑Fi Verbose"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Pag-throttle ng pag-scan ng Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Palaging aktibo ang mobile data"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardware acceleration para sa pag-tether"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Ipakita ang mga Bluetooth device na walang pangalan"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Hindi makakonekta"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Ipakita ang mga opsyon para sa certification ng wireless display"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Pataasin ang antas ng Wi‑Fi logging, ipakita sa bawat SSID RSSI sa Wi‑Fi Picker"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Binabawasan ang pagkaubos ng baterya at pinapahusay ang performance ng network"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Nakametro"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Hindi Nakametro"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Mga laki ng buffer ng Logger"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 7afb78a..6031eca 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Ağ işlemleri"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Kablosuz ekran sertifikası"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Kablosuz Ayrıntılı Günlük Kaydını etkinleştir"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Kablosuz ağ taramasını kısma"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobil veri her zaman etkin"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Tethering donanım hızlandırıcısı"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Adsız Bluetooth cihazlarını göster"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Bağlanılamadı"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Kablosuz ekran sertifikası seçeneklerini göster"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Kablosuz günlük kaydı seviyesini artır. Kablosuz Seçici\'de her bir SSID RSSI için göster."</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Pili daha az harcar ve ağ performansını iyileştirir"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Sayaçlı"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Sayaçsız"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Günlük Kaydedici arabellek boyutları"</string>
@@ -448,7 +450,7 @@
<string name="accessibility_manual_zen_more_time" msgid="1636187409258564291">"Daha uzun süre."</string>
<string name="accessibility_manual_zen_less_time" msgid="6590887204171164991">"Daha kısa süre."</string>
<string name="cancel" msgid="6859253417269739139">"İptal"</string>
- <string name="okay" msgid="1997666393121016642">"TAMAM"</string>
+ <string name="okay" msgid="1997666393121016642">"Tamam"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"Aç"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"Rahatsız Etmeyin\'i açın"</string>
<string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Hiçbir zaman"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 0808456..5f3c6a6 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Мережі"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Сертифікація бездрот. екрана"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Докладний запис у журнал Wi-Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Зменшити радіус пошуку мереж Wi‑Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Не вимикати мобільне передавання даних"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Апаратне прискорення під час використання телефона в режимі модема"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Показувати пристрої Bluetooth без назв"</string>
@@ -238,7 +239,7 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="6893955536658137179">"Активувати LDAC для аудіо Bluetooth\nВибір кодека: якість відтворення"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Трансляція: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Приватна DNS"</string>
- <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Виберіть режим \"Приватна DNS\""</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Режим приватної системи DNS"</string>
<string name="private_dns_mode_off" msgid="8236575187318721684">"Вимкнено"</string>
<string name="private_dns_mode_opportunistic" msgid="8314986739896927399">"Автоматично"</string>
<string name="private_dns_mode_provider" msgid="8354935160639360804">"Ім’я хосту приватного постачальника послуг DNS"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Не вдалося під’єднатися"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Показати параметри сертифікації бездротового екрана"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Показувати в журналі RSSI для кожного SSID під час вибору Wi-Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Зменшує споживання заряду акумулятора й підвищує ефективність роботи мережі"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"З тарифікацією трафіку"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Без тарифікації трафіку"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Розміри буфера журналу"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 8ab1c23..82d9b3f 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"نیٹ ورکنگ"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"وائرلیس ڈسپلے سرٹیفیکیشن"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi‑Fi وربوس لاگنگ فعال کریں"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi اسکین کو زبردستی روکا جا رہا ہے"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"موبائل ڈیٹا ہمیشہ فعال رکھیں"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"ٹیدرنگ ہارڈویئر سرعت کاری"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"بغیر نام والے بلوٹوتھ آلات دکھائیں"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"منسلک نہیں ہو سکا"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"وائرلیس ڈسپلے سرٹیفیکیشن کیلئے اختیارات دکھائیں"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi لاگنگ لیول میں اضافہ کریں، Wi‑Fi منتخب کنندہ میں فی SSID RSSI دکھائیں"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"بیٹری ڈرین کم کرتا ہے اور نیٹ ورک کارکردگی کو بہتر بناتا ہے"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"میٹرڈ"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"غیر میٹر شدہ"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"لاگر بفر کے سائز"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 2bcb450..5dca475 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Tarmoqlar"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Simsiz monitor sertifikatlari"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Batafsil Wi-Fi jurnali"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi tarmoqni taqsimlab skanlash"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Mobil internet doim yoniq tursin"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Modem rejimida apparatli tezlashtirish"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Bluetooth qurilmalarini nomlarisiz ko‘rsatish"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Ulanmadi"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Simsiz monitorlarni sertifikatlash parametrini ko‘rsatish"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fi ulanishini tanlashda har bir SSID uchun jurnalda ko‘rsatilsin"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Batareya sarfini tejaydi va tarmoq samaradorligini oshiradi"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Trafik hisoblanadigan tarmoq"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Trafik hisobi yuritilmaydigan tarmoq"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Jurnal buferi hajmi"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 2ac9a9b..4e2d6fa 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -35,7 +35,7 @@
<string name="wifi_not_in_range" msgid="1136191511238508967">"Ngoài vùng phủ sóng"</string>
<string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Sẽ không tự động kết nối"</string>
<string name="wifi_no_internet" msgid="4663834955626848401">"Không có quyền truy cập Internet"</string>
- <string name="saved_network" msgid="4352716707126620811">"Được lưu bởi <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="saved_network" msgid="4352716707126620811">"Do <xliff:g id="NAME">%1$s</xliff:g> lưu"</string>
<string name="connected_via_network_scorer" msgid="5713793306870815341">"Tự động được kết nối qua %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7867260222020343104">"Tự động được kết nối qua nhà cung cấp dịch vụ xếp hạng mạng"</string>
<string name="connected_via_passpoint" msgid="2826205693803088747">"Được kết nối qua %1$s"</string>
@@ -88,7 +88,7 @@
<string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Sử dụng để chia sẻ liên hệ"</string>
<string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Chia sẻ kết nối internet"</string>
<string name="bluetooth_profile_map" msgid="1019763341565580450">"Tin nhắn văn bản"</string>
- <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Quyền truy cập SIM"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Truy cập SIM"</string>
<string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Âm thanh HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Âm thanh HD"</string>
<string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"Thiết bị trợ thính"</string>
@@ -153,7 +153,7 @@
<string name="launch_defaults_some" msgid="313159469856372621">"Đã đặt một số ứng dụng chạy mặc định"</string>
<string name="launch_defaults_none" msgid="4241129108140034876">"Chưa đặt mặc định"</string>
<string name="tts_settings" msgid="8186971894801348327">"Cài đặt chuyển văn bản sang lời nói"</string>
- <string name="tts_settings_title" msgid="1237820681016639683">"Đầu ra văn bản thành giọng nói"</string>
+ <string name="tts_settings_title" msgid="1237820681016639683">"Chuyển văn bản sang lời nói"</string>
<string name="tts_default_rate_title" msgid="6030550998379310088">"Tốc độ lời nói"</string>
<string name="tts_default_rate_summary" msgid="4061815292287182801">"Tốc độ đọc văn bản"</string>
<string name="tts_default_pitch_title" msgid="6135942113172488671">"Độ cao"</string>
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Mạng"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Chứng nhận hiển thị không dây"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Bật ghi nhật ký chi tiết Wi‑Fi"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Hạn chế quét tìm Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Dữ liệu di động luôn hoạt động"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"Tăng tốc phần cứng khi chia sẻ kết nối"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Hiển thị các thiết bị Bluetooth không có tên"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Không thể kết nối"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Hiển thị tùy chọn chứng nhận hiển thị không dây"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Tăng mức ghi nhật ký Wi‑Fi, hiển thị mỗi SSID RSSI trong bộ chọn Wi‑Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Giảm hao pin và cải thiện hiệu suất mạng"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Đo lượng dữ liệu"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Không đo lượng dữ liệu"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Kích thước bộ đệm của trình ghi nhật ký"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 8685c10..460142c 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"网络"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"无线显示认证"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"启用 WLAN 详细日志记录功能"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"WLAN 扫描调节"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"始终开启移动数据网络"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"网络共享硬件加速"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"显示没有名称的蓝牙设备"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"无法连接"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"显示无线显示认证选项"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"提升 WLAN 日志记录级别(在 WLAN 选择器中显示每个 SSID 的 RSSI)"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"降低耗电量以及改善网络性能"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"按流量计费"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"不按流量计费"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"日志记录器缓冲区大小"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 653a39b..286765d 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"網絡"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"無線螢幕分享認證"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"啟用 Wi‑Fi 詳細記錄"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi‑Fi 掃瞄限流"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"一律保持啟用流動數據"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"網絡共享硬件加速"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"顯示沒有名稱的藍牙裝置"</string>
@@ -238,14 +239,15 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="6893955536658137179">"觸發藍牙音訊 LDAC\n編解碼器選項:播放品質"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"正在串流:<xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="select_private_dns_configuration_title" msgid="3700456559305263922">"私人 DNS"</string>
- <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"選取私人網域名稱系統 (DNS) 模式"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"選取私人 DNS 模式"</string>
<string name="private_dns_mode_off" msgid="8236575187318721684">"停用"</string>
<string name="private_dns_mode_opportunistic" msgid="8314986739896927399">"自動"</string>
- <string name="private_dns_mode_provider" msgid="8354935160639360804">"私人網域名稱系統 (DNS) 供應商主機名稱"</string>
- <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"輸入網域名稱系統 (DNS) 供應商的主機名稱"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"私人 DNS 供應商主機名稱"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"輸入 DNS 供應商主機名稱"</string>
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"無法連線"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"顯示無線螢幕分享認證的選項"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"讓 Wi‑Fi 記錄功能升級,在 Wi‑Fi 選擇器中依每個 SSID RSSI 顯示 Wi‑Fi 詳細紀錄"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"減低耗電量並改善網絡表現"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"按用量收費"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"不限數據用量收費"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"記錄器緩衝區空間"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 1201938..e85199f 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"網路連線"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"無線螢幕分享認證"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"啟用 Wi‑Fi 詳細記錄設定"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"Wi-Fi 掃描調節"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"行動數據連線一律保持啟用狀態"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"數據連線硬體加速"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"顯示沒有名稱的藍牙裝置"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"無法連線"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"顯示無線螢幕分享認證的選項"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"讓 Wi‑Fi 記錄功能升級,在 Wi‑Fi 選擇器中依每個 SSID RSSI 顯示 Wi‑Fi 詳細記錄"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"降低耗電量以及改善網路效能"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"計量付費"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"非計量付費"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"記錄器緩衝區空間"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 9ea73fa..1bfbf07 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -220,6 +220,7 @@
<string name="debug_networking_category" msgid="7044075693643009662">"Ukunethiwekha"</string>
<string name="wifi_display_certification" msgid="8611569543791307533">"Ukunikezwa isitifiketi sokubukeka okungenantambo"</string>
<string name="wifi_verbose_logging" msgid="4203729756047242344">"Nika amandlaukungena kwe-Wi-Fi Verbose"</string>
+ <string name="wifi_scan_throttling" msgid="160014287416479843">"I-throttling yokuskena kwe-Wi-Fi"</string>
<string name="mobile_data_always_on" msgid="8774857027458200434">"Idatha yeselula ihlala isebenza"</string>
<string name="tethering_hardware_offload" msgid="7470077827090325814">"I-Tethering hardware acceleration"</string>
<string name="bluetooth_show_devices_without_names" msgid="4708446092962060176">"Bonisa amadivayisi e-Bluetooth ngaphandle kwamagama"</string>
@@ -246,6 +247,7 @@
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Ayikwazanga ukuxhuma"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Bonisa izinketho zokunikeza isitifiketi ukubukeka okungenantambo"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"khuphula izinga lokungena le-Wi-Fi, bonisa nge-SSID RSSI engayodwana kusikhethi se-Wi-Fi"</string>
+ <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"Yehlisa ukuphela kwebhethri futhi ithuthukise ukusebenza kwenethiwekhi"</string>
<string name="wifi_metered_label" msgid="4514924227256839725">"Kulinganisiwe"</string>
<string name="wifi_unmetered_label" msgid="6124098729457992931">"Akulinganiselwa"</string>
<string name="select_logd_size_title" msgid="7433137108348553508">"Amasayizi weloga ngebhafa"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index f30de13..ea3c1d9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -110,13 +110,7 @@
}
public DataUsageInfo getDataUsageInfo() {
- final String subscriberId = getActiveSubscriberId();
- if (subscriberId == null) {
- return warn("no subscriber id");
- }
- NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriberId);
- template = NetworkTemplate.normalize(template, getTelephonyManager()
- .getMergedSubscriberIds());
+ NetworkTemplate template = DataUsageUtils.getMobileTemplate(mContext, mSubscriptionId);
return getDataUsageInfo(template);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
new file mode 100644
index 0000000..de38e8a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 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.content.Context;
+import android.net.NetworkTemplate;
+import android.os.ParcelUuid;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utils class for data usage
+ */
+public class DataUsageUtils {
+ private static final String TAG = "DataUsageUtils";
+
+ /**
+ * Return mobile NetworkTemplate based on {@code subId}
+ */
+ public static NetworkTemplate getMobileTemplate(Context context, int subId) {
+ final TelephonyManager telephonyManager = context.getSystemService(
+ TelephonyManager.class);
+ final SubscriptionManager subscriptionManager = context.getSystemService(
+ SubscriptionManager.class);
+ final SubscriptionInfo info = subscriptionManager.getActiveSubscriptionInfo(subId);
+ final NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll(
+ telephonyManager.getSubscriberId(subId));
+
+ if (info == null) {
+ Log.i(TAG, "Subscription is not active: " + subId);
+ return mobileAll;
+ }
+ final ParcelUuid groupUuid = info.getGroupUuid();
+ if (groupUuid == null) {
+ Log.i(TAG, "Subscription doesn't have valid group uuid: " + subId);
+ return mobileAll;
+ }
+
+ // Otherwise merge other subscriberId to create new NetworkTemplate
+ final List<SubscriptionInfo> groupInfos = subscriptionManager.getSubscriptionsInGroup(
+ groupUuid);
+ final List<String> mergedSubscriberIds = new ArrayList<>();
+ for (SubscriptionInfo subInfo : groupInfos) {
+ final String subscriberId = telephonyManager.getSubscriberId(
+ subInfo.getSubscriptionId());
+ if (subscriberId != null) {
+ mergedSubscriberIds.add(subscriberId);
+ }
+ }
+ return NetworkTemplate.normalize(mobileAll, mergedSubscriberIds.toArray(new String[0]));
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
new file mode 100644
index 0000000..dc33cfe
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 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 com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.NetworkTemplate;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class DataUsageUtilsTest {
+
+ private static final int SUB_ID = 1;
+ private static final int SUB_ID_2 = 2;
+ private static final String SUBSCRIBER_ID = "Test Subscriber";
+ private static final String SUBSCRIBER_ID_2 = "Test Subscriber 2";
+
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private SubscriptionManager mSubscriptionManager;
+ @Mock
+ private SubscriptionInfo mInfo1;
+ @Mock
+ private SubscriptionInfo mInfo2;
+ @Mock
+ private ParcelUuid mParcelUuid;
+ private Context mContext;
+ private List<SubscriptionInfo> mInfos;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(RuntimeEnvironment.application);
+ when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+ when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
+ when(mTelephonyManager.getSubscriberId(SUB_ID)).thenReturn(SUBSCRIBER_ID);
+ when(mTelephonyManager.getSubscriberId(SUB_ID_2)).thenReturn(SUBSCRIBER_ID_2);
+ when(mInfo1.getSubscriptionId()).thenReturn(SUB_ID);
+ when(mInfo2.getSubscriptionId()).thenReturn(SUB_ID_2);
+
+ mInfos = new ArrayList<>();
+ mInfos.add(mInfo1);
+ mInfos.add(mInfo2);
+ when(mSubscriptionManager.getSubscriptionsInGroup(mParcelUuid)).thenReturn(mInfos);
+ }
+
+ @Test
+ public void getMobileTemplate_infoNull_returnMobileAll() {
+ when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(null);
+
+ final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID);
+ assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue();
+ assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isFalse();
+ }
+
+ @Test
+ public void getMobileTemplate_groupUuidNull_returnMobileAll() {
+ when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1);
+ when(mInfo1.getGroupUuid()).thenReturn(null);
+
+ final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID);
+ assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue();
+ assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isFalse();
+ }
+
+ @Test
+ public void getMobileTemplate_groupUuidExist_returnMobileMerged() {
+ when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(mInfo1);
+ when(mInfo1.getGroupUuid()).thenReturn(mParcelUuid);
+
+ final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID);
+ assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue();
+ assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isTrue();
+ }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index f7132e3..d07bc32 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3043,15 +3043,16 @@
// Increment the generation first, so observers always see the new value
mGenerationRegistry.incrementGeneration(key);
- if (isGlobalSettingsKey(key)) {
+ if (isGlobalSettingsKey(key) || isConfigSettingsKey(key)) {
final long token = Binder.clearCallingIdentity();
try {
- if (Global.LOCATION_GLOBAL_KILL_SWITCH.equals(name)) {
+ if (Global.LOCATION_GLOBAL_KILL_SWITCH.equals(name)
+ && isGlobalSettingsKey(key)) {
// When the global kill switch is updated, send the
// change notification for the location setting.
notifyLocationChangeForRunningUsers();
}
- notifyGlobalSettingChangeForRunningUsers(key, name);
+ notifySettingChangeForRunningUsers(key, name);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -3091,7 +3092,7 @@
}
}
- private void notifyGlobalSettingChangeForRunningUsers(int key, String name) {
+ private void notifySettingChangeForRunningUsers(int key, String name) {
// Important: No need to update generation for each user as there
// is a singleton generation entry for the global settings which
// is already incremented be the caller.
diff --git a/packages/SystemUI/res/drawable/ic_emergency_star.xml b/packages/SystemUI/res/drawable/ic_emergency_star.xml
new file mode 100644
index 0000000..148f250
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_emergency_star.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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.0"
+ android:viewportHeight="24.0">
+ <path
+ android:pathData="M9.321,2l0,5.359l-4.641,-2.68l-2.68,4.641l4.641,2.679l-4.641,2.68l2.68,4.641l4.641,-2.68l0,5.359l5.359,0l0,-5.359l4.641,2.68l2.68,-4.641l-4.641,-2.68l4.641,-2.679l-2.68,-4.641l-4.641,2.679l0,-5.359z"
+ android:strokeColor="#00000000"
+ android:fillColor="#EA4335"
+ android:strokeWidth="1"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
deleted file mode 100644
index b7b21fa..0000000
--- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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.
--->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="#242424" /> <!-- 14% of white -->
- <padding android:paddingTop="@dimen/ongoing_appops_chip_bg_padding"
- android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding" />
- <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/global_actions_column.xml b/packages/SystemUI/res/layout-land/global_actions_column.xml
index 99a4e13..98cb0d0 100644
--- a/packages/SystemUI/res/layout-land/global_actions_column.xml
+++ b/packages/SystemUI/res/layout-land/global_actions_column.xml
@@ -42,10 +42,10 @@
android:orientation="horizontal"
android:layout_marginTop="@dimen/global_actions_grid_side_margin"
android:translationZ="@dimen/global_actions_translate"
- android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
- android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
- android:paddingTop="@dimen/global_actions_grid_vertical_padding"
- android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+ android:paddingLeft="@dimen/global_actions_grid_vertical_padding"
+ android:paddingRight="@dimen/global_actions_grid_vertical_padding"
+ android:paddingTop="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingBottom="@dimen/global_actions_grid_horizontal_padding"
android:background="?android:attr/colorBackgroundFloating"
/>
<!-- For separated items-->
@@ -55,10 +55,10 @@
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/global_actions_grid_side_margin"
android:layout_marginTop="@dimen/global_actions_grid_side_margin"
- android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
- android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
- android:paddingTop="@dimen/global_actions_grid_vertical_padding"
- android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+ android:paddingLeft="@dimen/global_actions_grid_vertical_padding"
+ android:paddingRight="@dimen/global_actions_grid_vertical_padding"
+ android:paddingTop="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingBottom="@dimen/global_actions_grid_horizontal_padding"
android:orientation="horizontal"
android:background="?android:attr/colorBackgroundFloating"
android:translationZ="@dimen/global_actions_translate"
diff --git a/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml b/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml
index 0f86131..5e7604c 100644
--- a/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml
+++ b/packages/SystemUI/res/layout-land/global_actions_column_seascape.xml
@@ -41,10 +41,10 @@
android:orientation="horizontal"
android:layout_marginBottom="@dimen/global_actions_grid_side_margin"
android:translationZ="@dimen/global_actions_translate"
- android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
- android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
- android:paddingTop="@dimen/global_actions_grid_vertical_padding"
- android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+ android:paddingLeft="@dimen/global_actions_grid_vertical_padding"
+ android:paddingRight="@dimen/global_actions_grid_vertical_padding"
+ android:paddingTop="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingBottom="@dimen/global_actions_grid_horizontal_padding"
android:background="?android:attr/colorBackgroundFloating"
/>
<!-- For separated items-->
@@ -55,10 +55,10 @@
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/global_actions_grid_side_margin"
android:layout_marginBottom="@dimen/global_actions_grid_side_margin"
- android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
- android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
- android:paddingTop="@dimen/global_actions_grid_vertical_padding"
- android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+ android:paddingLeft="@dimen/global_actions_grid_vertical_padding"
+ android:paddingRight="@dimen/global_actions_grid_vertical_padding"
+ android:paddingTop="@dimen/global_actions_grid_horizontal_padding"
+ android:paddingBottom="@dimen/global_actions_grid_horizontal_padding"
android:orientation="horizontal"
android:background="?android:attr/colorBackgroundFloating"
android:translationZ="@dimen/global_actions_translate"
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
deleted file mode 100644
index dce9ce1..0000000
--- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?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.
--->
-
-
-<com.android.systemui.privacy.OngoingPrivacyChip
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/privacy_chip"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- android:layout_gravity="center_vertical|end"
- android:gravity="center_vertical"
- android:orientation="horizontal"
- android:focusable="true" >
-
- <FrameLayout
- android:id="@+id/background"
- android:layout_height="@dimen/ongoing_appops_chip_height"
- android:minWidth="48dp"
- android:layout_width="wrap_content" >
- <LinearLayout
- android:id="@+id/icons_container"
- android:layout_height="match_parent"
- android:layout_width="wrap_content"
- android:gravity="center_vertical"
- />
- </FrameLayout>
-</com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_text_item.xml b/packages/SystemUI/res/layout/ongoing_privacy_text_item.xml
deleted file mode 100644
index 5595b13..0000000
--- a/packages/SystemUI/res/layout/ongoing_privacy_text_item.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
--->
-
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textDirection="locale"
- android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary"
-/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml
index c812489..683e867 100644
--- a/packages/SystemUI/res/layout/quick_settings_header_info.xml
+++ b/packages/SystemUI/res/layout/quick_settings_header_info.xml
@@ -21,6 +21,7 @@
android:layout_below="@id/quick_status_bar_system_icons"
android:paddingStart="@dimen/status_bar_padding_start"
android:paddingEnd="@dimen/status_bar_padding_end"
+ android:visibility="invisible"
android:theme="@style/QSHeaderTheme">
<com.android.systemui.qs.QSHeaderInfoLayout
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index cd9f780..54fb216 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -22,19 +22,11 @@
android:layout_height="@*android:dimen/quick_qs_offset_height"
android:clipChildren="false"
android:clipToPadding="false"
- android:gravity="center"
android:orientation="horizontal"
android:clickable="true"
android:paddingStart="@dimen/status_bar_padding_start"
android:paddingEnd="@dimen/status_bar_padding_end" >
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:orientation="horizontal"
- android:gravity="center_vertical|start" >
-
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:layout_width="wrap_content"
@@ -46,23 +38,4 @@
android:singleLine="true"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
systemui:showDark="false" />
- </LinearLayout>
-
- <android.widget.Space
- android:id="@+id/space"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_gravity="center_vertical|center_horizontal"
- android:visibility="gone" />
-
- <LinearLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:orientation="horizontal"
- android:gravity="center_vertical|end" >
-
- <include layout="@layout/ongoing_privacy_chip" />
-
- </LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index e6579da..80cba56 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -647,13 +647,10 @@
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Hou aan waarsku"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Skakel kennisgewings af"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Hou aan om kennisgewings van hierdie program af te wys?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Lig"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Geprioritiseerd"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Help jou om te fokus met kennisgewings wat net op die aftrekskerm is. Altyd stil."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Wys prioriteitkennisgewings hieronder. Altyd stil."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Wys prioriteitkennisgewings hieronder. Altyd stil."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Wys prioriteitkennisgewings hieronder. Altyd stil."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Trek jou aandag met klank en \'n statusbalk-ikoon. Wys op sluitskerm."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Stil"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Waarskuwings"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Help jou om te fokus sonder klank of vibrasie."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Kry jou aandag met klank of vibrasie."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Hierdie kennisgewings kan nie gewysig word nie."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Hierdie groep kennisgewings kan nie hier opgestel word nie"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Instaanbediener-kennisgewing"</string>
@@ -935,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Beweeg na links onder"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Beweeg na regs onder"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Maak toe"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 4789789..b779ce7 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"የዩኤስቢ እርማት አይፈቀድም"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"አሁን ወደዚህ መሣሪያ የገባው ተጠቃሚ የዩኤስቢ እርማትን ማብራት አይችልም። ይህን ባህሪ ለመጠቀም ወደ ዋና ተጠቃሚ ይቀይሩ።"</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"የዩኤስቢ ወደብ ተሰናክሏል"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"መሣሪያዎን ከፈሳሽ ወይም ፍርስራሽ ለመጠበቅ ሲባል የዩኤስቢ ወደቡ ተሰናክሏል፣ እና ማናቸውም ተቀጥላዎችን አያገኝም።\n\nየዩኤስቢ ወደቡን እንደገና መጠቀም ችግር በማይኖረው ጊዜ ማሳወቂያ ይደርሰዎታል።"</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"ኃይል መሙያዎችን እና ተጨማሪ መሣሪያዎችን ፈልጎ ለማግኘት የነቃ የዩኤስቢ ወደብ"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"ዩኤስቢ አንቃ"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"የበለጠ መረዳት"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"ማያ እንዲሞላ አጉላ"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"ማያ ለመሙለት ሳብ"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"ቅጽበታዊ ገጽ እይታ"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"ዳግመኛ አታሳይ"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"ሁሉንም አጽዳ"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"ያቀናብሩ"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"ጸጥ ያሉ ማሳወቂያዎች"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"ሁሉንም ጸጥ ያሉ ማሳወቂያዎችን ያጽዱ"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"ማሳወቂያዎች በአትረብሽ ባሉበት ቆመዋል"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"አሁን ጀምር"</string>
<string name="empty_shade_text" msgid="708135716272867002">"ምንም ማሳወቂያ የለም"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"አግድ"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"ማሳየትን ቀጥል"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"አሳንስ"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"ፀጥ ያለ"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"ጸጥ እንዳለ ቆይ"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"ማንቃት"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"ማንቃቱን ቀጥል"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"ማሳወቂያዎችን አጥፋ"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"ከዚህ መተግበሪያ ማሳወቂያዎችን ማሳየት ይቀጥል?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"ረጋ ያለ"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"ቅድሚያ የተሰጠው"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"በወደታች ተጎችታች ጥላ ውስጥ ብቻ በማሳወቂያዎች ላይ እንዲያተኩሩ ያግዝዎታል። ሁልጊዜ ጸጥታ።"</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"ከዚህ በታች ቅድሚያ ተሰጪ ማሳወቂያዎችን ያሳያል። ሁልጊዜ ጸጥታ።"</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"ከዚህ በታች ቅድሚያ ተሰጪ ማሳወቂያዎችን ያሳያል። ሁልጊዜ ጸጥታ።"</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"ከዚህ በታች ቅድሚያ ተሰጪ ማሳወቂያዎችን ያሳያል። ሁልጊዜ ጸጥታ።"</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"ከድምፅ እና የሁኔታ አሞሌ አዶ ጋር የእርስዎን ትኩረት ይስባል። በማያ ገጽ ቁልፍ ላይ ያሳያል።"</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"ፀጥ ያለ"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"ማንቃት"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"ያለ ድምፅ ወይም ንዝረት እርስዎ ትኩረት እንዲያደርጉ ያግዛል።"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"ከድምፅ ወይም ንዝረት ጋር የእርስዎን ትኩረት ይስባል።"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"እነዚህ ማሳወቂያዎች ሊሻሻሉ አይችሉም።"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"የማሳወቂያዎች ይህ ቡድን እዚህ ላይ ሊዋቀር አይችልም"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"ተኪ ማሳወቂያ"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"ፍቀድ"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"ከልክል"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"ለባትሪ ቆጣቢ መርሐግብርን ለማስያዝ መታ ያድርጉ"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"ባትሪው የማለቅ ዕድሉ ከፍ ያለ ከሆነ ያብሩት"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"አይ፣ አመሰግናለሁ"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"የባትሪ ቆጣቢ መርሐግብር በርቷል"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"ባትሪ ልክ ከ<xliff:g id="PERCENTAGE">%d</xliff:g>%% በታች ሲሆን ባትሪ ቆጣቢ በራስ-ሰር ይበራል።"</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"የግርጌውን ግራ አንቀሳቅስ"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"ታችኛውን ቀኝ ያንቀሳቅሱ"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"አሰናብት"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 42879a4..2381e16 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"لا يُسمح بتصحيح أخطاء USB"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"لا يمكن للمستخدم الذي يسجّل دخوله حاليًا إلى هذا الجهاز تشغيل تصحيح أخطاء USB. لاستخدام هذه الميزة، يمكنك التبديل إلى المستخدم الأساسي."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"تمّ إيقاف منفذ USB"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"لحماية جهازك من السوائل أو الشوائب، سيتمّ إيقاف منفذ USB ولن يتم رصد أيّ ملحقات.\n\nوسيتمّ إعلامك عندما يُسمح باستخدام منفذ USB مرة أخرى."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"تم تفعيل منفذ USB لاكتشاف أجهزة الشحن والملحقات."</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"تفعيل USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"مزيد من المعلومات"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"تكبير/تصغير لملء الشاشة"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"توسيع بملء الشاشة"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"لقطة شاشة"</string>
@@ -470,10 +468,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"عدم الإظهار مرة أخرى"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"محو الكل"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"إدارة"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"الإشعارات الصامتة"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"محو جميع الإشعارات الصامتة"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"تم إيقاف الإشعارات مؤقتًا وفقًا لإعداد \"الرجاء عدم الإزعاج\""</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"البدء الآن"</string>
<string name="empty_shade_text" msgid="708135716272867002">"ليس هناك أي اشعارات"</string>
@@ -657,21 +653,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"حظر"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"الاستمرار في تلقّي الإشعارات"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"تصغير"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"صامت"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"متابعة عرض الإشعارات بدون صوت"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"تنبيه"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"متابعة إرسال التنبيهات"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"إيقاف الإشعارات"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"هل تريد الاستمرار في تلقي إشعارات من هذا التطبيق؟"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"الإشعارات الهادئة"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"لها الأولوية"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"لمساعدتك على التركيز، يتم عرض هذه الإشعارات ضمن \"مركز الإشعارات\" فقط. كتم الصوت دائمًا."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"يتم عرض هذه الإشعارات تحت الإشعارات ذات الأولوية، مع كتم صوتها دائمًا."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"يتم عرض هذه الإشعارات تحت الإشعارات ذات الأولوية، مع كتم صوتها دائمًا."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"يتم عرض هذه الإشعارات تحت الإشعارات ذات الأولوية، مع كتم صوتها دائمًا."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"عند تلقّي هذه الإشعارات، سيظهر رمز في شريط الحالة مع صوت للفت انتباهك. ويتم عرض هذه الإشعارات على شاشة التأمين."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"إشعار صامت"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"إشعار مصحوب بتنبيه صوتي"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"يساعدك هذا الإشعار على التركيز بدون صوت أو اهتزاز."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"يلفت هذا الإشعار انتباهك باستخدام الصوت والاهتزاز."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"يتعذّر تعديل هذه الإشعارات."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"يتعذّر ضبط مجموعة الإشعارات هذه هنا."</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"إشعار مستند إلى خادم وكيل"</string>
@@ -928,8 +919,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"سماح"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"رفض"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"انقر لجدولة \"توفير شحن البطارية\"."</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"فعِّل الميزة إذا كان من المرجح نفاد شحن البطارية."</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"لا، شكرًا"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"تم تفعيل جدولة \"توفير شحن البطارية\"."</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"سيتم تفعيل ميزة \"توفير شحن البطارية\" عندما تنخفض البطارية عن <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -962,4 +952,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"نقل إلى أسفل يمين الشاشة"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"نقل إلى أسفل اليسار"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"تجاهل"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index de2b933..7f349eb 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"ইউএছবি ডিবাগিঙৰ অনুমতি নাই"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"এই ডিভাইচটোত বর্তমান ছাইন ইন হৈ থকা ব্যৱহাৰকাৰীজনে ইউএছবি ডিবাগিং অন কৰিব নোৱাৰে। এই সুবিধাটো ব্যৱহাৰ কৰিবলৈ হ\'লে মুখ্য ব্যৱহাৰকাৰী হিচাপে ছাইন ইন কৰক।"</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"ইউএছবি প’ৰ্ট অক্ষম কৰা হ’ল"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"আপোনাৰ ডিভাইচটো তৰল বা ধূলি-মাকতিৰ পৰা ৰক্ষা কৰিবলৈ ইউএছবি প’ৰ্টটো অক্ষম কৰি ৰখা হৈছে ফলত ই কোনো আনুষংগিক সামগ্ৰী ধৰা পেলাব নোৱাৰে।\n\nযেতিয়া ইউএছবি প’ৰ্টটো নিৰাপদভাৱে ব্যৱহাৰ কৰিব পৰা হ’ব তেতিয়া আপোনাক জনোৱা হ’ব।"</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"চাৰ্জাৰ আৰু আনুষংগিক সামগ্ৰী চিনাক্ত কৰিবলৈ USB প’ৰ্ট সক্ষম কৰা হ’ল"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB সক্ষম কৰক"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"অধিক জানক"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"স্ক্ৰীণ পূর্ণ কৰিবলৈ জুম কৰক"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"স্ক্ৰীণ পূর্ণ কৰিবলৈ প্ৰসাৰিত কৰক"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"স্ক্ৰীণশ্বট"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"পুনৰাই নেদেখুৱাব"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"সকলো মচক"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"পৰিচালনা"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"নীৰৱ জাননীসমূহ"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"সকলো নীৰৱ জাননী মচক"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"অসুবিধা নিদিব-ই জাননী পজ কৰিছে"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"এতিয়াই আৰম্ভ কৰক"</string>
<string name="empty_shade_text" msgid="708135716272867002">"কোনো জাননী নাই"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"অৱৰোধ কৰক"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"দেখুওৱাই থাকক"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"সৰু কৰক"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"নিৰৱ"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"নীৰৱ হৈ থাকক"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"সতৰ্ক কৰক"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"সতৰ্ক কৰি থাকক"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"জাননী অফ কৰক"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"এই এপটোৰ জাননী দেখুওৱাই থাকিব লাগিবনে?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"সাধাৰণ"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"অগ্ৰাধিকাৰপ্ৰাপ্ত"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"আপুনি যাতে অসুবিধা নাপায়, তাৰ বাবে কেৱল পুল-ডাউন শ্বেডত এই জাননী দেখুৱায়। সদায় নিৰৱ।"</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"অগ্ৰাধিকাৰপ্ৰাপ্ত জাননীসমূহৰ তলত দেখুৱায়। সদায় নিৰৱ।"</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"অগ্ৰাধিকাৰপ্ৰাপ্ত জাননীসমূহৰ তলত দেখুৱায়। সদায় নিৰৱ।"</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"অগ্ৰাধিকাৰপ্ৰাপ্ত জাননীসমূহৰ তলত দেখুৱায়। সদায় নিৰৱ।"</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"ধ্বনি আৰু স্থিতি দণ্ডৰ আইকনৰ জৰিয়তে আপোনাৰ মনোযোগ আকৰ্ষণ কৰে। লক স্ক্ৰীণত দেখুৱায়।"</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"নীৰৱ"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"সতৰ্ক কৰক"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"কোনো ধ্বনি অথবা কম্পন অবিহনে আপোনাক মনোযোগ দিয়াত সহায় কৰে।"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"ধ্বনি অথবা কম্পনৰ জৰিয়তে আপোনাৰ মনোযোগ আকৰ্ষণ কৰে।"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"এই জাননীসমূহ সংশোধন কৰিব নোৱাৰি।"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"এই ধৰণৰ জাননীবোৰ ইয়াত কনফিগাৰ কৰিব পৰা নাযায়"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"প্ৰক্সি হিচাপে পঠিওৱা জাননী"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"অনুমতি দিয়ক"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"অস্বীকাৰ কৰক"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"বেটাৰি সঞ্চয়কাৰীৰ সময়সূচী সক্ৰিয় কৰিবলৈ টিপক"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"বেটাৰি শেষ হোৱাৰ সম্ভাৱনা থাকিলে অন কৰক"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"নালাগে, ধন্যবাদ"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"বেটাৰি সঞ্চয়কাৰীৰ সময়সূচী অন কৰা অৱস্থাত আছে"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"বেটাৰি চ্চাৰ্জৰ স্তৰ <xliff:g id="PERCENTAGE">%d</xliff:g>%%তকৈ কম হোৱাৰ লগে লগে বেটাৰি সঞ্চয়কাৰী স্বয়ংক্ৰিয়ভাৱে অন হ’ব।"</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"বুটামটো বাওঁফালে নিয়ক"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"তলৰ সোঁফালে নিয়ক"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"অগ্ৰাহ্য কৰক"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index f27bc4b..4602bd2 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB debaq prosesinə icazə verilmir"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Hazırda bu cihaza daxil olmuş istifadəçi USB sazlama prosesini aktiv edə bilməz. Bu funksiyadan istifadə etmək üçün əsas istifadəçi hesaba daxil olmalıdır."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB portu deaktiv edildi"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"USB portu deaktivdir. Cihazı maye və zərbədən qorumaq üçün aksesuar aşkarlanmayacaq.\n\nUSB portu yenidən istifadə üçün təhlükəsiz olduqda bildiriş göndəriləcək."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Adapter və aksesuarları aşkarlamaq üçün USB portu aktiv edildi"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB-ni aktiv edin"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Ətraflı məlumat"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Ekranı doldurmaq üçün yaxınlaşdır"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Ekranı doldurmaq üçün uzat"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Skrinşot"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Daha göstərmə"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Hamısını silin"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"İdarə edin"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Səssiz bildirişlər"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Səssiz bildirişlərin hamısını silin"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Bildirişlər \"Narahat Etməyin\" rejimi tərəfindən dayandırıldı"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"İndi başlayın"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Heç bir bildiriş yoxdur"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Blok edin"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Göstərməyə davam edin"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Kiçildin"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Səssiz"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Səssiz göstərilsin"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Siqnal"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Xəbərdarlıq göndərməyə davam edin"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Bildirişləri deaktiv edin"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Bu tətbiqin bildirişləri göstərilməyə davam edilsin?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Daha az əhəmiyyətli"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Mühüm"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Yalnız aşağı açılan siyahıda bildirişlərə fokuslanmağınıza kömək edir. Həmişə səssiz."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Mühüm bildirişlərin aşağısında göstərilir. Həmişə səssiz."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Mühüm bildirişlərin aşağısında göstərilir. Həmişə səssiz."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Mühüm bildirişlərin aşağısında göstərilir. Həmişə səssiz."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Səs & status paneli ikonası ilə diqqətinizi cəlb edir. Ekran kilidini göstərir."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Səssiz"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Siqnal"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Səs və ya vibrasiya olmadan fokuslanmağınıza kömək edir."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Səs və ya vibrasiya ilə diqqətinizi çəkir."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Bu bildirişlər dəyişdirilə bilməz."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Bu bildiriş qrupunu burada konfiqurasiya etmək olmaz"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Proksi bildirişi"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"İcazə verin"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Rədd edin"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Batareya Qənaətini planlaşdırmaq üçün klikləyin"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Batareya bitmək üzrə olduqda aktiv edin"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Xeyr, təşəkkür"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Batareya Qənaəti aktivdir"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Batareya <xliff:g id="PERCENTAGE">%d</xliff:g>%%-dən aşağı düşdükdə Batareya Qənaəti avtomatik aktiv ediləcək."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Aşağıya sola köçürün"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Aşağıya sağa köçürün"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Kənarlaşdırın"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index afe84d0..75be041 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Otklanjanje grešaka na USB-u nije dozvoljeno"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Korisnik koji je trenutno prijavljen na ovaj uređaj ne može da uključi otklanjanje grešaka na USB-u. Da biste koristili ovu funkciju, prebacite na primarnog korisnika."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB port je onemogućen"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Da bi se uređaj zaštitio od tečnosti ili nečistoće, USB port je onemogućen i neće otkrivati dodatnu opremu.\n\nObavestićemo vas kada ponovo budete mogli da koristite USB port."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB port je omogućen radi otkrivanja punjača i dodatne opreme"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Omogući USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Saznajte više"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zumiraj na celom ekranu"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Razvuci na ceo ekran"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Snimak ekrana"</string>
@@ -461,10 +459,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ne prikazuj ponovo"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Obriši sve"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Upravljajte"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Nečujna obaveštenja"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Obrišite sva nečujna obaveštenja"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Obaveštenja su pauzirana režimom Ne uznemiravaj"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Započni odmah"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Nema obaveštenja"</string>
@@ -648,21 +644,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Blokiraj"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Nastavi da prikazuješ"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Umanji"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Nečujno"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Ne uključuj zvuk"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Upozoravanje"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Nastavi sa obaveštenjima"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Isključi obaveštenja"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Želite li da se obaveštenja iz ove aplikacije i dalje prikazuju?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Diskretno"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioritetno"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Pomaže vam da se fokusirate. Obaveštenja su samo na padajućoj traci. Uvek nečujno."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Prikazuje se ispod prioritetnih obaveštenja. Uvek nečujno."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Prikazuje se ispod prioritetnih obaveštenja. Uvek nečujno."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Prikazuje se ispod prioritetnih obaveštenja. Uvek nečujno."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Privlači vam pažnju pomoću zvuka i ikone statusne trake. Prikazuje se na zaključanom ekranu."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Nečujno"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Upozoravanje"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Pomaže vam da se koncentrišete bez zvuka ili vibracije."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Privlači vam pažnju pomoću zvuka ili vibracije."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Ova obaveštenja ne mogu da se menjaju."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Ova grupa obaveštenja ne može da se konfiguriše ovde"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Obaveštenje preko proksija"</string>
@@ -913,8 +904,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Dozvoli"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Odbij"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Dodirnite da biste napravili raspored za uštedu baterije"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Uključite ako će baterija verovatno da se isprazni"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Ne, hvala"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Raspored za uštedu baterije je uključen"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Ušteda baterije će se automatski uključivati kada baterija padne ispod <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -947,4 +937,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Premesti dole levo"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Premesti dole desno"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Odbaci"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 4c459f2..2155b33 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Адладка USB не дапускаецца"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Карыстальнік, які зараз увайшоў у гэту прыладу, не можа ўключыць адладку USB. Каб выкарыстоўваць гэту функцыю, пераключыцеся на асноўнага карыстальніка."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Порт USB адключаны"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Порт USB адключаны, каб засцерагчы прыладу ад вадкасці і смецця, таму дадатковае абсталяванне не будзе выяўлена.\n\nВы атрымаеце апавяшчэнне, калі порт USB можна будзе выкарыстоўваць зноў."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB-порту дазволена вызначаць зарадныя прылады і аксесуары"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Уключыць USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Даведацца больш"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Павял. на ўвесь экран"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Расцягн. на ўвесь экран"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Здымак экрана"</string>
@@ -466,10 +464,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Не паказваць зноў"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Ачысціць усё"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Кіраваць"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Апавяшчэнні без гуку"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Выдаліць усе апавяшчэнні без гуку"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Паказ апавяшчэнняў прыпынены ў рэжыме \"Не турбаваць\""</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Пачаць зараз"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Апавяшчэнняў няма"</string>
@@ -653,21 +649,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Заблакіраваць"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Працягваць паказваць"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Згарнуць"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Бязгучны рэжым"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Не ўключаць гук"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Апавяшчаць"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Апавяшчаць далей"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Выключыць апавяшчэнні"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Працягваць паказваць апавяшчэнні гэтай праграмы?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Ціхі рэжым"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Прыярытэтныя"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Дапамагае не адцягваць увагу, паказваючы апавяшчэнні толькі на апушчанай шторцы. Заўсёды без гуку."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Паказвае ўнізе апавяшчэнні з высокім прыярытэтам. Заўсёды без гуку."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Паказвае ўнізе апавяшчэнні з высокім прыярытэтам. Заўсёды без гуку."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Паказвае ўнізе апавяшчэнні з высокім прыярытэтам. Заўсёды без гуку."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Прываблівае ўвагу гукам і значком на панэлі стану. Паказвае на экране блакіроўкі."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Бязгучны рэжым"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Абвесткі"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Не адцягвае ўвагу дзякуючы выключаным гуку і вібрацыі."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Прыцягвае ўвагу гукам і вібрацыяй."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Гэтыя апавяшчэнні нельга змяніць."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Тут канфігурыраваць гэту групу апавяшчэнняў забаронена"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Праксіраванае апавяшчэнне"</string>
@@ -920,8 +911,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Дазволіць"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Адмовіць"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Націсніце, каб уключыць рэжым эканоміі зараду"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Уключыце, калі зарад акумулятара заканчваецца"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Не, дзякуй"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Уключаны рэжым эканоміі зараду"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Калі ўзровень зараду акумулятара знізіцца да <xliff:g id="PERCENTAGE">%d</xliff:g>%%, аўтаматычна ўключыцца рэжым эканоміі энергіі."</string>
@@ -954,4 +944,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Перамясціць лявей і ніжэй"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Перамясціць правей і ніжэй"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Адхіліць"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index ee07f5d..1e619a8 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Отстраняването на грешки през USB не е разрешено"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Потребителят, който понастоящем е влязъл в това устройство, не може да включи функцията за отстраняване на грешки през USB. За да я използвате, превключете към основния потребител."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB портът е деактивиран"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"С цел защита на устройството ви от течности и замърсяване USB портът е деактивиран и няма да открива аксесоари.\n\nЩе получите известие, когато можете отново да използвате USB порта."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB портът може да разпознава зарядни устройства и аксесоари"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Активиране на USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Научете повече"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Мащаб – запълва екрана"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Разпъване – запълва екрана"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Екранна снимка"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Да не се показва отново"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Изчистване на всички"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Управление"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Беззвучни известия"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Изчистване на всички беззвучни известия"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Известията са поставени на пауза от режима „Не безпокойте“"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Стартиране сега"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Няма известия"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Блокиране"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Да продължат да се показват"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Намаляване"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Тих режим"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Показване на известията без звук"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Сигнализиране"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Продължаване на сигнализирането"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Изключване на известията"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Да продължат ли да се показват известията от това приложение?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Ненатрапчиви известия"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Приоритетни известия"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Помага ви да се съсредоточите, като известията се показват само в падащия панел. Винаги в тих режим."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Показва се под приоритетните известия. Винаги в тих режим."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Показва се под приоритетните известия. Винаги в тих режим."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Показва се под приоритетните известия. Винаги в тих режим."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Привлича вниманието ви със звук и икона в лентата на състоянието. Показва се на заключения екран."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Тих режим"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Сигнализиране"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Помага ви да се фокусирате без звук или вибриране."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Привлича вниманието ви със звук или вибриране."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Тези известия не могат да бъдат променяни."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Тази група от известия не може да бъде конфигурирана тук"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Известие, получено чрез делегиране"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Разрешаване"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Отказ"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Докоснете, за да активирате автоматичния режим за запазване на батерията"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Включване, когато е вероятно батерията да се изтощи"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Не, благодаря"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Включен е автоматичен режим за запазване на батерията"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Режимът за запазване на батерията ще се включи автоматично, след като нивото й премине под <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Преместване долу вляво"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Преместване долу вдясно"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Отхвърляне"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index c04093a..9904341 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB ডিবাগিং অনুমোদিত নয়"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ব্যবহারকারী এখন এই ডিভাইসে সাইন-ইন করেছেন তাই USB ডিবাগিং চালু করা যাবে না। এই বৈশিষ্ট্যটি ব্যবহার করতে, প্রাথমিক ব্যবহারকারীতে পাল্টে নিন।"</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"ইউএসবি পোর্ট বন্ধ করা হয়েছে"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"কোনও তরল পদার্থ ও ধুলো থেকে আপনার ডিভাইসকে সুরক্ষিত রাখতে, ইউএসবি পোর্ট বন্ধ করা আছে, তাই কোনও অ্যাক্সেসরির শনাক্ত করা যাবে না।\n\nইউএসবি পোর্ট আবার ব্যবহার করা নিরাপদ হলে, আপনাকে জানানো হবে।"</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"চার্জার ও আনুষঙ্গিক আইটেম শনাক্ত করার জন্য ইউএসবি চালু করা হয়েছে"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"ইউএসবি চালু করুন"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"আরও জানুন"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"স্ক্রীণ পূরণ করতে জুম করুন"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"ফুল স্ক্রিন করুন"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"স্ক্রিনশট নিন"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"আর দেখাবেন না"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"সবকিছু সাফ করুন"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"পরিচালনা করুন"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"নীরব বিজ্ঞপ্তি"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"সব নীরব বিজ্ঞপ্তি মুছুন"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"\'বিরক্ত করবেন না\' দিয়ে বিজ্ঞপ্তি পজ করা হয়েছে"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"এখন শুরু করুন"</string>
<string name="empty_shade_text" msgid="708135716272867002">"কোনো বিজ্ঞপ্তি নেই"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"ব্লক করুন"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"দেখতে থাকুন"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"ছোট করে দিন"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"নীরব রাখুন"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"বিজ্ঞপ্তি মিউট করুন"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"সতর্ক করুন"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"বিজ্ঞপ্তি পান"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"বিজ্ঞপ্তি বন্ধ করুন"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"এই অ্যাপের বিজ্ঞপ্তি পরেও দেখে যেতে চান?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"সাইলেন্ট মোডে"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"অগ্রাধিকার দেওয়া হয়েছে"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"আপনি যাতে বিরক্ত না হন, তার জন্য শুধুমাত্র পুল-ডাউন শেডে এই বিজ্ঞপ্তি দেখায়। সবসময় সাইলেন্ট।"</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"অগ্রাধিকারযুক্ত বিজ্ঞপ্তির নিচে দেখানো হয়। সবসময় সাইলেন্ট।"</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"অগ্রাধিকারযুক্ত বিজ্ঞপ্তির নিচে দেখানো হয়। সবসময় সাইলেন্ট।"</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"অগ্রাধিকারযুক্ত বিজ্ঞপ্তির নিচে দেখানো হয়। সবসময় সাইলেন্ট।"</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"সাউন্ড ও স্ট্যাটাস বার আইকনের সাহায্যে দৃষ্টি আকর্ষণ করে। এটি লক স্ক্রিনে দেখা যায়।"</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"সাইলেন্ট"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"সতর্ক করুন"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"সাউন্ড বা ভাইব্রেশন ছাড়া ফোকাস করতে আপনাকে সাহায্য করে।"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"সাউন্ড বা ভাইব্রেশনের সাহায্যে দৃষ্টি আকর্ষণ করে।"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"এই বিজ্ঞপ্তিগুলি পরিবর্তন করা যাবে না।"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"এই সমস্ত বিজ্ঞপ্তিকে এখানে কনফিগার করা যাবে না"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"প্রক্সি করা বিজ্ঞপ্তি"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"অনুমতি দিন"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"খারিজ করুন"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"ব্যাটারি সেভার চালু হওয়ার সময় সেট করতে ট্যাপ করুন"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"ব্যাটারির চার্জ শেষ হয়ে যাওয়ার সম্ভাবনা দেখা দিলে চালু করুন"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"না থাক"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"আগে সেট করা সময় অনুযায়ী ব্যাটারি সেভার চালু হয়েছে"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"চার্জ <xliff:g id="PERCENTAGE">%d</xliff:g>%%-এর নিচে চলে গেলে ব্যাটারি সেভার নিজে থেকেই চালু হয়ে যাবে।"</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"নিচে বাঁদিকে সরান"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"নিচে ডান দিকে সরান"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"খারিজ করুন"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index e0ef1bd..8a02f16 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Otklanjanje grešaka putem uređaja spojenog na USB nije dozvoljeno"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Korisnik koji je trenutno prijavljen na ovaj uređaj ne može uključiti opciju za otklanjanje grešaka koristeći USB. Da koristite tu funkciju, prebacite se na primarnog korisnika."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB priključak je onemogućen"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"USB priključak je onemogućen kako bi se vaš uređaj zaštitio od tečnosti i nečistoća i neće detektirati priključene uređaje.\n\nDobit ćete obavještenje kada ponovo bude sigurno koristiti USB priključak."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB priključak je omogućen za prepoznavanje punjača i pribora"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Omogući USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Saznajte više"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Uvećaj prikaz na ekran"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Razvuci prikaz na ekran"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Snimak ekrana"</string>
@@ -461,10 +459,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ne prikazuj opet"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Očisti sve"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Upravljaj"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Nečujna obavještenja"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Obriši sva nečujna obavještenja"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Obavještenja su pauzirana načinom rada Ne ometaj"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Započni odmah"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Nema obavještenja"</string>
@@ -650,21 +646,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Blokiraj"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Nastavi prikazivanje"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimiziraj"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Nečujno"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Ostani u nečujnom načinu rada"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Upozorenja"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Nastavi upozoravati"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Isključi obavještenja"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Nastaviti prikazivanje obavještenja iz ove aplikacije?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Diskretno"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioritetno"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Pomaže vam da se fokusirate prikazujući obavještenja samo na padajućoj traci. Uvijek nečujno."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Prikazuje se ispod prioritetnih obavještenja. Uvijek nečujno."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Prikazuje se ispod prioritetnih obavještenja. Uvijek nečujno."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Prikazuje se ispod prioritetnih obavještenja. Uvijek nečujno."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Privlači vašu pažnju pomoću zvuka i ikone na statusnoj traci. Prikazuje se na zaključanom ekranu."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Nečujno"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Upozorenja"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Pomaže vam da se koncentrirate bez zvuka ili vibracije."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Privlači vašu pažnju pomoću zvuka ili vibracije."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Ta obavještenja se ne mogu izmijeniti."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Ovdje nije moguće konfigurirati ovu grupu obavještenja"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Obavještenje preko proksi servera"</string>
@@ -915,8 +906,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Dozvoli"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Odbij"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Dodirnite da zakažete Uštedu baterije"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Uključite ako je vjerovatno da će se baterija istrošiti"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Ne, hvala"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Zakazivanje Uštede baterije je uključeno"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Kada je baterija ispod <xliff:g id="PERCENTAGE">%d</xliff:g>%%, Ušteda baterije se automatski uključuje."</string>
@@ -949,4 +939,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Pomjeri dolje lijevo"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Pomjerite dolje desno"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Odbaci"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 19be829..4c52722 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"No es permet la depuració per USB"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"L\'usuari que té iniciada la sessió al dispositiu en aquest moment no pot activar la depuració per USB. Per utilitzar aquesta funció, cal canviar a l\'usuari principal."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"El port USB està desactivat"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Per protegir el teu dispositiu dels líquids o de la pols, el port USB s\'ha desactivat i no detectarà cap accessori.\n\nRebràs una notificació quan puguis tornar a utilitzar-lo."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"S\'ha activat el port USB per detectar carregadors i accessoris"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Activa l\'USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Més informació"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom per omplir pantalla"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Estira per omplir pant."</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Captura de pantalla"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"No ho tornis a mostrar"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Esborra-ho tot"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Gestió"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Notificacions silencioses"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Esborra totes les notificacions silencioses"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notificacions pausades pel mode No molestis"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Comença ara"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Cap notificació"</string>
@@ -546,7 +542,7 @@
<string name="screen_pinning_description_recents_invisible_accessible" msgid="6134833683151189507">"Aquest element es continuarà mostrant fins que deixis de fixar-lo. Per fer-ho, mantén premut el botó d\'inici."</string>
<string name="screen_pinning_toast" msgid="2266705122951934150">"Per deixar de fixar aquesta pantalla, mantén premuts els botons Enrere i Aplicacions recents"</string>
<string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"Per deixar de fixar aquesta pantalla, mantén premuts els botons Enrere i Inici"</string>
- <string name="screen_pinning_positive" msgid="3783985798366751226">"D\'acord"</string>
+ <string name="screen_pinning_positive" msgid="3783985798366751226">"Entesos"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"No, gràcies"</string>
<string name="screen_pinning_start" msgid="1022122128489278317">"S\'ha fitxat la pantalla"</string>
<string name="screen_pinning_exit" msgid="5187339744262325372">"S\'ha deixat de fixar la pantalla"</string>
@@ -610,7 +606,7 @@
<string name="tuner_warning_title" msgid="7094689930793031682">"Diversió per a uns quants, però no per a tothom"</string>
<string name="tuner_warning" msgid="8730648121973575701">"El Personalitzador d\'interfície d\'usuari presenta opcions addicionals per canviar i personalitzar la interfície d\'usuari d\'Android. És possible que aquestes funcions experimentals canviïn, deixin de funcionar o desapareguin en versions futures. Continua amb precaució."</string>
<string name="tuner_persistent_warning" msgid="8597333795565621795">"És possible que aquestes funcions experimentals canviïn, deixin de funcionar o desapareguin en versions futures. Continua amb precaució."</string>
- <string name="got_it" msgid="2239653834387972602">"D\'acord"</string>
+ <string name="got_it" msgid="2239653834387972602">"Entesos"</string>
<string name="tuner_toast" msgid="603429811084428439">"Enhorabona! El Personalitzador d\'interfície d\'usuari s\'ha afegit a Configuració."</string>
<string name="remove_from_settings" msgid="8389591916603406378">"Treu de Configuració"</string>
<string name="remove_from_settings_prompt" msgid="6069085993355887748">"Vols suprimir el Personalitzador d\'interfície d\'usuari de Configuració i deixar d\'utilitzar-ne totes les funcions?"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Bloqueja"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Continua rebent"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimitza"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Silencioses"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Continua silenciant"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Alertes"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Continua avisant-me"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Desactiva les notificacions"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Vols continuar rebent notificacions d\'aquesta aplicació?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Discreta"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioritàries"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"T\'ajuda a concentrar-te perquè les notificacions només es mostren a l\'àrea desplegable. Sempre en silenci."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Es mostren a sota de les notificacions prioritàries. Sempre silencioses."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Es mostren a sota de les notificacions prioritàries. Sempre silencioses."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Es mostren a sota de les notificacions prioritàries. Sempre silencioses."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Atrauen la teva atenció amb un so i una icona a la barra d\'estat. Es mostren a la pantalla de bloqueig."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Silenci"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Alertes"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"T\'ajuda a concentrar-te sense so ni vibració."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Atrau la teva atenció amb so i vibració."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Aquestes notificacions no es poden modificar."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Aquest grup de notificacions no es pot configurar aquí"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Notificació mitjançant aplicació intermediària"</string>
@@ -908,13 +899,12 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Permet"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Denega"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Toca per programar l\'estalvi de bateria"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Activa\'l quan sigui probable que et quedis sense bateria"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"No"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"S\'ha activat la programació de l\'estalvi de bateria"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"L\'estalvi de bateria s\'activarà automàticament quan el nivell de bateria sigui inferior al <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
<string name="open_saver_setting_action" msgid="8314624730997322529">"Configuració"</string>
- <string name="auto_saver_okay_action" msgid="2701221740227683650">"D\'acord"</string>
+ <string name="auto_saver_okay_action" msgid="2701221740227683650">"Entesos"</string>
<string name="heap_dump_tile_name" msgid="9141031328971226374">"Aboca espai de SysUI"</string>
<string name="ongoing_privacy_chip_content_single_app" msgid="4479560741898690064">"<xliff:g id="APP">%1$s</xliff:g> està fent servir el següent: <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8640691753867990511">"Algunes aplicacions estan fent servir el següent: <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Mou a baix a l\'esquerra"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Mou a baix a la dreta"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Omet"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 266ba15..4afe0c5 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Ladění přes USB není povoleno"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Uživatel aktuálně přihlášený k tomuto zařízení nemůže zapnout ladění přes USB. Chcete-li tuto funkci použít, přepněte na primárního uživatele."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Port USB je deaktivován"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Kvůli ochraně vašeho zařízení před tekutinami a nečistotami je port USB zakázán a nerozpozná žádné příslušenství.\n\nAž bude opět bezpečné port USB použít, budeme vás informovat."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Port USB může zjišťovat nabíječky a příslušenství"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Aktivovat USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Další informace"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Přiblížit na celou obrazovku"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Na celou obrazovku"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Snímek obrazovky"</string>
@@ -464,10 +462,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Tuto zprávu příště nezobrazovat"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Smazat vše"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Spravovat"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Tichá oznámení"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Vymazat všechna tichá oznámení"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Oznámení jsou pozastavena režimem Nerušit"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Spustit"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Žádná oznámení"</string>
@@ -651,21 +647,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Blokovat"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Nadále zobrazovat"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimalizovat"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Tiché"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Nadále bez zvuku"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Upozorňovat"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Dál upozorňovat"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Vypnout oznámení"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Mají se oznámení z této aplikace nadále zobrazovat?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Nenápadná"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioritní"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Budete se moci lépe soustředit, protože oznámení se budou zobrazovat pouze na vysouvacím panelu. Vždy tichý režim."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Níže zobrazit prioritní oznámení. Vždy tichý režim."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Níže zobrazit prioritní oznámení. Vždy tichý režim."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Níže zobrazit prioritní oznámení. Vždy tichý režim."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Upozorní vás pomocí zvuku a ikony na stavovém řádku. Zobrazit na obrazovce uzamčení."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Ticho"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Upozornění"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Pomáhá vám soustředit se vypnutím zvuku a vibrací."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Upozorňuje vás pomocí zvuku a vibrací."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Tato oznámení nelze upravit."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Tuto skupinu oznámení tady nelze nakonfigurovat"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Zprostředkované oznámení"</string>
@@ -918,8 +909,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Povolit"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Zamítnout"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Klepnutím naplánujete aktivování spořiče baterie"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Zapnout, když bude pravděpodobné, že se vybije baterie"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Ne, díky"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Plánované aktivování spořiče baterie je zapnuté"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Spořič baterie se automaticky aktivuje, jakmile baterie klesne pod <xliff:g id="PERCENTAGE">%d</xliff:g> %%."</string>
@@ -952,4 +942,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Přesunout vlevo dolů"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Přesunout vpravo dolů"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Zavřít"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 0bf19be..702fdcf 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB-fejlretning er ikke tilladt"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Den bruger, der i øjeblikket er logget ind på denne enhed, kan ikke aktivere USB-fejlretning. Skift til den primære bruger for at bruge denne funktion."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB-porten er deaktiveret"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"USB-porten er blevet deaktiveret for at beskytte din enhed mod væske og snavs. Den kan derfor ikke registrere noget tilbehør.\n\nDu får besked, når du kan bruge USB-porten igen."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB-porten er aktiveret for at registrere opladere og tilbehør"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Aktivér USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Få flere oplysninger"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom til fuld skærm"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Stræk til fuld skærm"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
@@ -100,7 +98,7 @@
<string name="use_ptp_button_title" msgid="7517127540301625751">"Isæt som et kamera (PTP)"</string>
<string name="installer_cd_button_title" msgid="2312667578562201583">"Installer appen Android Filoverførsel til Mac"</string>
<string name="accessibility_back" msgid="567011538994429120">"Tilbage"</string>
- <string name="accessibility_home" msgid="8217216074895377641">"Startskærm"</string>
+ <string name="accessibility_home" msgid="8217216074895377641">"Hjem"</string>
<string name="accessibility_menu" msgid="316839303324695949">"Menu"</string>
<string name="accessibility_accessibility_button" msgid="7601252764577607915">"Hjælpefunktioner"</string>
<string name="accessibility_rotate_button" msgid="7402949513740253006">"Roter skærmen"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Vis ikke igen"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Ryd alle"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Administrer"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Lydløse notifikationer"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Ryd alle lydløse notifikationer"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notifikationer er sat på pause af Forstyr ikke"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Start nu"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Ingen notifikationer"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Bloker"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Fortsæt med at vise notifikationer"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimer"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Lydløs"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Fortsæt med lydløse notifikationer"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Underretninger"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Fortsæt med at underrette"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Deaktiver notifikationer"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Vil du fortsætte med at se notifikationer fra denne app?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Diskret"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioriteret"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Hjælper dig med at fokusere på de vigtige ting, da notifikationerne kun vises i panelet, der trækkes ned. Altid lydløs."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Viser notifikationer med lav prioritet. Altid lydløs."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Viser notifikationer med lav prioritet. Altid lydløs."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Viser notifikationer med lav prioritet. Altid lydløs."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Fanger din opmærksomhed med lyd og ikoner i statusbjælken. Vises på låseskærmen."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Lydløs"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Underretninger"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Ingen lyde eller vibrationer, der forstyrrer dig."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Fanger din opmærksomhed med lyd eller vibration."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Disse notifikationer kan ikke redigeres."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Du kan ikke konfigurere denne gruppe notifikationer her"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Proxyforbundet notifikation"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Tillad"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Afvis"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Tryk for at fastsætte en tidsplan for batterisparefunktionen"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Aktivér, når det ser ud til, at batteriet løber tør"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Nej tak"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Tidsplanen for batterisparefunktionen er aktiveret"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Batterisparefunktionen aktiveres automatisk, når batteriniveauet når under <xliff:g id="PERCENTAGE">%d</xliff:g> %%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Flyt ned til venstre"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Flyt ned til højre"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Afvis"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 5c21ea3..a1a01df 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB-Debugging nicht zulässig"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Der momentan auf diesem Gerät angemeldete Nutzer kann das USB-Debugging nicht aktivieren. Um diese Funktion verwenden zu können, wechsle zum primären Nutzer."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB-Port deaktiviert"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Zum Schutz deines Geräts vor Flüssigkeiten oder Fremdkörpern ist der USB-Port zurzeit deaktiviert und erkennt kein Zubehör.\n\nDu wirst benachrichtigt, wenn der USB-Port wieder verwendet werden kann."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Erkennung von Ladegeräten und Zubehör am USB-Port aktiviert"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB aktivieren"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Weitere Informationen"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom auf Bildschirmgröße"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Auf Bildschirmgröße anpassen"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
@@ -462,10 +460,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Nicht mehr anzeigen"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Alle löschen"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Verwalten"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Lautlose Benachrichtigungen"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Alle lautlosen Benachrichtigungen löschen"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Benachrichtigungen durch \"Bitte nicht stören\" pausiert"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Jetzt starten"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Keine Benachrichtigungen"</string>
@@ -542,7 +538,7 @@
<string name="volume_odi_captions_hint_enable" msgid="49750248924730302">"aktivieren"</string>
<string name="volume_odi_captions_hint_disable" msgid="8980842810619956593">"deaktivieren"</string>
<string name="accessibility_output_chooser" msgid="8185317493017988680">"Ausgabegerät wechseln"</string>
- <string name="screen_pinning_title" msgid="3273740381976175811">"Bildschirm ist fixiert"</string>
+ <string name="screen_pinning_title" msgid="3273740381976175811">"Der Bildschirm ist angpinnt"</string>
<string name="screen_pinning_description" msgid="8909878447196419623">"Der Bildschirm bleibt so lange eingeblendet, bis du die Fixierung aufhebst. Berühre und halte dazu \"Zurück\" und \"Übersicht\"."</string>
<string name="screen_pinning_description_recents_invisible" msgid="8281145542163727971">"Der Bildschirm wird so lange angezeigt, bis du die Fixierung aufhebst. Berühre und halte dazu \"Zurück\" und \"Startbildschirm\"."</string>
<string name="screen_pinning_description_gestural" msgid="1191513974909607884">"Der Bildschirm wird so lange angezeigt, bis du die Fixierung aufhebst. Dazu wischst du nach oben und hältst den Bildschirm gedrückt"</string>
@@ -552,7 +548,7 @@
<string name="screen_pinning_toast_recents_invisible" msgid="8252402309499161281">"Um die Fixierung für diesen Bildschirm aufzuheben, berühre und halte \"Zurück\" und \"Startbildschirm\""</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"Ok"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Nein danke"</string>
- <string name="screen_pinning_start" msgid="1022122128489278317">"Bildschirm fixiert"</string>
+ <string name="screen_pinning_start" msgid="1022122128489278317">"Der Bildschirm ist angepinnt"</string>
<string name="screen_pinning_exit" msgid="5187339744262325372">"Fixierung für Bildschirm aufgehoben"</string>
<string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g> ausblenden?"</string>
<string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Sie wird wieder eingeblendet, wenn du sie in den Einstellungen erneut aktivierst."</string>
@@ -649,21 +645,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Blockieren"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Weiterhin anzeigen"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimieren"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Lautlos"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Weiter lautlos bleiben"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Benachrichtigen"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Weiterhin Benachrichtigungen senden"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Benachrichtigungen deaktivieren"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Benachrichtigungen dieser App weiterhin anzeigen?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Stumm"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Priorisiert"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Benachrichtigungen erscheinen nur in der Benachrichtigungsleiste. So kannst du dich besser konzentrieren. Immer lautlos."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Erscheinen unter den wichtigen Benachrichtigungen. Immer lautlos."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Erscheinen unter den wichtigen Benachrichtigungen. Immer lautlos."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Erscheinen unter den wichtigen Benachrichtigungen. Immer lautlos."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Benachrichtigungen werden mit einem Ton und einem Statusleistensymbol angekündigt. Erscheinen auf dem Sperrbildschirm."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Lautlos"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Benachrichtigen"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Benachrichtigungen werden ohne Ton oder Vibration angekündigt, um deine Konzentration nicht zu stören."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Benachrichtigungen werden mit einem Ton oder einer Vibration angekündigt, um dich auf sie aufmerksam zu machen."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Diese Benachrichtigungen können nicht geändert werden."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Die Benachrichtigungsgruppe kann hier nicht konfiguriert werden"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Weitergeleitete Benachrichtigung"</string>
@@ -912,8 +903,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Zulassen"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Ablehnen"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Tippen zum Planen des Energiesparmodus"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Aktivieren, wenn der Akku wahrscheinlich nicht mehr lange hält"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Nein danke"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Geplanter Energiesparmodus aktiviert"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Der Energiesparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %% automatisch aktiviert."</string>
@@ -946,4 +936,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Nach unten links verschieben"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Nach unten rechts verschieben"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Schließen"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 775f9b1..0a87078 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -647,13 +647,10 @@
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Να συνεχιστούν οι ειδοποιήσεις"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Απενεργοποίηση ειδοποιήσεων"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Να συνεχίσουν να εμφανίζονται ειδοποιήσεις από αυτήν την εφαρμογή;"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Διακριτικές"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Προτεραιότητας"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Σας βοηθάει να συγκεντρωθείτε με ειδοποιήσεις που εμφανίζονται μόνο στο αναπτυσσόμενο πλαίσιο σκίασης. Πάντα σε σίγαση."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Εμφανίζεται κάτω από ειδοποιήσεις προτεραιότητας. Πάντα σε σίγαση."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Εμφανίζεται κάτω από ειδοποιήσεις προτεραιότητας. Πάντα σε σίγαση."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Εμφανίζεται κάτω από ειδοποιήσεις προτεραιότητας. Πάντα σε σίγαση."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Τραβάει την προσοχή σας με ήχους και ένα εικονίδιο γραμμής κατάστασης. Εμφανίζεται στην οθόνη κλειδώματος."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Σίγαση"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Ειδοποίηση"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Σας βοηθά να συγκεντρωθείτε χωρίς ήχο και δόνηση."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Τραβά την προσοχή σας με ήχο ή δόνηση."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Δεν είναι δυνατή η τροποποίηση αυτών των ειδοποιήσεων"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Δεν είναι δυνατή η διαμόρφωση αυτής της ομάδας ειδοποιήσεων εδώ"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Ειδοποίηση μέσω διακομιστή μεσολάβησης"</string>
@@ -935,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Μετακίνηση κάτω αριστερά"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Μετακίνηση κάτω δεξιά"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Παράβλεψη"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index ed2c0a07..2b836ec 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -647,13 +647,10 @@
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Keep alerting"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Turn off notifications"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Gentle"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioritised"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Helps you focus with notifications only in the pull-down shade. Always silent."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Displays below priority notifications. Always silent."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Displays below priority notifications. Always silent."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Displays below priority notifications. Always silent."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Gets your attention with sound and a status bar icon. Shows on lock screen."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Silent"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Alerting"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Helps you focus without sound or vibration."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Gets your attention with sound or vibration."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"These notifications can\'t be modified."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"This group of notifications cannot be configured here"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Proxied notification"</string>
@@ -935,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Move bottom left"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Move bottom right"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Dismiss"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index c67a491..3409611 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -647,13 +647,10 @@
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Keep alerting"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Turn off notifications"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Gentle"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioritised"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Helps you focus with notifications only in the pull-down shade. Always silent."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Displays below priority notifications. Always silent."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Displays below priority notifications. Always silent."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Displays below priority notifications. Always silent."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Gets your attention with sound and a status bar icon. Shows on lock screen."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Silent"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Alerting"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Helps you focus without sound or vibration."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Gets your attention with sound or vibration."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"These notifications can\'t be modified."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"This group of notifications cannot be configured here"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Proxied notification"</string>
@@ -935,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Move bottom left"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Move bottom right"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Dismiss"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index ed2c0a07..2b836ec 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -647,13 +647,10 @@
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Keep alerting"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Turn off notifications"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Gentle"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioritised"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Helps you focus with notifications only in the pull-down shade. Always silent."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Displays below priority notifications. Always silent."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Displays below priority notifications. Always silent."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Displays below priority notifications. Always silent."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Gets your attention with sound and a status bar icon. Shows on lock screen."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Silent"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Alerting"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Helps you focus without sound or vibration."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Gets your attention with sound or vibration."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"These notifications can\'t be modified."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"This group of notifications cannot be configured here"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Proxied notification"</string>
@@ -935,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Move bottom left"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Move bottom right"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Dismiss"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index ed2c0a07..2b836ec 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -647,13 +647,10 @@
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Keep alerting"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Turn off notifications"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Gentle"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioritised"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Helps you focus with notifications only in the pull-down shade. Always silent."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Displays below priority notifications. Always silent."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Displays below priority notifications. Always silent."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Displays below priority notifications. Always silent."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Gets your attention with sound and a status bar icon. Shows on lock screen."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Silent"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Alerting"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Helps you focus without sound or vibration."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Gets your attention with sound or vibration."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"These notifications can\'t be modified."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"This group of notifications cannot be configured here"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Proxied notification"</string>
@@ -935,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Move bottom left"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Move bottom right"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Dismiss"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index d5c5a44..416931d 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -647,13 +647,10 @@
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Keep alerting"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Turn off notifications"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Keep showing notifications from this app?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Gentle"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioritized"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Helps you focus with notifications only in the pull-down shade. Always silent."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Displays below priority notifications. Always silent."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Displays below priority notifications. Always silent."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Displays below priority notifications. Always silent."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Gets your attention with sound & a status bar icon. Shows on lock screen."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Silent"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Alerting"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Helps you focus without sound or vibration."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Gets your attention with sound or vibration."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"These notifications can\'t be modified."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"This group of notifications cannot be configured here"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Proxied notification"</string>
@@ -935,4 +932,6 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Move bottom left"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Move bottom right"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Dismiss"</string>
+ <string name="notification_content_system_nav_changed" msgid="7218093915747788444">"System navigation updated. To make changes, go to Settings."</string>
+ <string name="notification_content_gesture_nav_available" msgid="8111130443656460792">"Go to Settings to update system navigation"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 924e596..96dd605 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"No tienes permitida la depuración por USB"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"El usuario al que accediste en este dispositivo no puede activar la depuración por USB. Para usar esta función, debes cambiar al usuario principal."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Puerto USB inhabilitado"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Para proteger tu dispositivo de líquidos o suciedad, el puerto USB está inhabilitado y no detectará ningún accesorio.\n\nTe avisaremos cuando puedas usar el puerto USB de nuevo."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Se habilitó el puerto USB para detectar cargadores y accesorios"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Habilitar USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Más información"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom para ocupar la pantalla"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Estirar p/ ocupar la pantalla"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Captura de pantalla"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"No volver a mostrar"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Borrar todo"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Administrar"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Notificaciones silenciosas"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Borrar todas las notificaciones silenciosas"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notificaciones pausadas por el modo \"No interrumpir\""</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Comenzar ahora"</string>
<string name="empty_shade_text" msgid="708135716272867002">"No hay notificaciones"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Bloquear"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Seguir viendo"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizar"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Silencio"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Silenciar notificaciones"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Alertas"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Seguir recibiendo alertas"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Desactivar notificaciones"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"¿Quieres seguir viendo las notificaciones de esta app?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Discretas"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Priorizadas"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Te ayuda a concentrarte, ya que las notificaciones se muestran solo como banner desplegable. Siempre en silencio."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Se muestra debajo de las notificaciones de prioridad. Siempre en silencio."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Se muestra debajo de las notificaciones de prioridad. Siempre en silencio."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Se muestra debajo de las notificaciones de prioridad. Siempre en silencio."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Notificaciones que llaman la atención con sonido y un ícono en la barra de estado. Se muestra en la pantalla bloqueada."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Silencio"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Alertas"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Te ayuda a concentrarte sin sonar ni vibrar."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Capta tu atención con sonido o vibración."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"No se pueden modificar estas notificaciones."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"No se puede configurar aquí este grupo de notificaciones"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Notificación almacenada en proxy"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Permitir"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Rechazar"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Presiona para programar el Ahorro de batería"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Actívalo cuando la batería se esté por acabar"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"No, gracias"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Se activó la programación del Ahorro de batería"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"El Ahorro de batería se activará automáticamente cuando quede menos de <xliff:g id="PERCENTAGE">%d</xliff:g>%% de batería."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Ubicar abajo a la izquierda"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Ubicar abajo a la derecha"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Ignorar"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 25611bd..2129bd8 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Depuración USB no permitida"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"El usuario con el que se ha iniciado sesión en este dispositivo no puede activar la depuración USB. Para utilizar esta función, inicia sesión con la cuenta de usuario principal."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Puerto USB inhabilitado"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Para proteger tu dispositivo de los líquidos y la suciedad, el puerto USB se ha inhabilitado y no detectará ningún accesorio.\n\nRecibirás una notificación cuando puedas volver a usarlo."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Puerto USB habilitado para detectar cargadores y accesorios"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Habilitar USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Más información"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom para ajustar"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Expandir para ajustar"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Captura de pantalla"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"No volver a mostrar"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Borrar todo"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Gestionar"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Notificaciones silenciadas"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Borrar todas las notificaciones silenciadas"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notificaciones pausadas por el modo No molestar"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Iniciar ahora"</string>
<string name="empty_shade_text" msgid="708135716272867002">"No hay notificaciones"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Bloquear"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Seguir mostrando"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizar"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Silencio"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Silenciar notificaciones"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Alertas"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Quiero seguir recibiendo alertas"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Desactivar notificaciones"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"¿Quieres seguir viendo las notificaciones de esta aplicación?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Discretas"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Priorizadas"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Las notificaciones solo se muestran en la pantalla desplegable para que puedas concentrarte. Siempre en silencio."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Muestra las notificaciones con prioridad baja. Siempre en silencio."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Muestra las notificaciones con prioridad baja. Siempre en silencio."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Muestra las notificaciones con prioridad baja. Siempre en silencio."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Llama tu atención con sonido y un icono en la barra de estado. Se muestra en la pantalla de bloqueo."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Silencio"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Alertas"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Te ayuda a concentrarte sin sonido ni vibración."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Llama tu atención con sonido o vibración."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Estas notificaciones no se pueden modificar."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Este grupo de notificaciones no se puede configurar aquí"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Notificación mediante proxy"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Permitir"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Denegar"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Tocar para programar el modo Ahorro de batería"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Activar cuando sea probable que se quede sin batería"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"No, gracias"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Se ha activado la programación del modo Ahorro de batería"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"El modo Ahorro de batería se activará automáticamente cuando quede menos de un <xliff:g id="PERCENTAGE">%d</xliff:g> %% de batería."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Mover abajo a la izquierda."</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Mover abajo a la derecha"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Cerrar"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 2864128..c9219e7 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB-silumine pole lubatud"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Sellesse seadmesse praegu sisse logitud kasutaja ei saa USB-silumist sisse lülitada. Selle funktsiooni kasutamiseks vahetage peamisele kasutajale."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB-port on keelatud"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Selleks et kaitsta teie seadet vedeliku või mustuse eest, on USB-port keelatud ja see ei tuvasta lisatarvikuid.\n\nKui USB-porti tohib taas kasutada, saate selle kohta märguande."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB-pordil on lubatud tuvastada laadijaid ja tarvikuid"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Luba USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Lisateave"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Suumi ekraani täitmiseks"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Venita ekraani täitmiseks"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Ekraanipilt"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ära kuva uuesti"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Tühjenda kõik"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Haldamine"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Hääletud märguanded"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Kustuta kõik hääletud märguanded"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Režiim Mitte segada peatas märguanded"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Alusta kohe"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Märguandeid pole"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Blokeeri"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Jätka kuvamist"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimeeri"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Hääletu"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Kuva helita"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Teavitamine"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Teavita ka edaspidi"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Lülita märguanded välja"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Kas jätkata selle rakenduse märguannete kuvamist?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Leebe"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioriseeritud"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Aitab teil allatõmmatavas menüüs ainult märguannetele keskenduda. Alati vaikne."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Kuvatakse prioriteetsete märguannete all. Alati vaikne."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Kuvatakse prioriteetsete märguannete all. Alati vaikne."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Kuvatakse prioriteetsete märguannete all. Alati vaikne."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Haarab teie tähelepanu heli ja olekuriba ikooni abil. Kuvatakse lukustuskuval."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Hääletu"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Teavitamine"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Aitab teil keskenduda (heli või vibreerimine puudub)."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Haarab heli või vibreerimisega teie tähelepanu."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Neid märguandeid ei saa muuta."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Seda märguannete rühma ei saa siin seadistada"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Puhvriga märguanne"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Luba"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Keela"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Puudutage akusäästja ajastamiseks"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Lülitatakse sisse, kui aku hakkab tühjaks saama"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Tänan, ei"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Automaatne akusäästja on sisse lülitatud"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Kui aku tase langeb alla <xliff:g id="PERCENTAGE">%d</xliff:g>%%, lülitub akusäästja automaatselt sisse."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Teisalda alla vasakule"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Teisalda alla paremale"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Loobu"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 1feb1b1..9dea61f 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Ez da onartzen USB arazketa"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Gailu honetan saioa hasita duen erabiltzaileak ezin du aktibatu USB arazketa. Eginbide hori erabiltzeko, aldatu erabiltzaile nagusira."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Desgaitu egin da USB ataka"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"USB ataka desgaitu egin da gailua likido edo zikinkeriengandik babesteko, eta ez du hautemango osagarririk.\n\nJakinarazpen bat jasoko duzu USB ataka berriz erabiltzeko moduan dagoenean."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB ataka gaitu da kargagailuak eta osagarriak hautemateko"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Gaitu USB ataka"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Lortu informazio gehiago"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Handiagotu pantaila betetzeko"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Luzatu pantaila betetzeko"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Pantaila-argazkia"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ez erakutsi berriro"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Garbitu guztiak"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Kudeatu"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Soinurik gabeko jakinarazpenak"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Garbitu soinurik gabeko jakinarazpen guztiak"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"\"Ez molestatu\" moduak pausatu egin ditu jakinarazpenak"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Hasi"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Ez dago jakinarazpenik"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Blokeatu"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Jarraitu erakusten"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizatu"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Soinurik gabe"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Jarraitu isilik"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Alertak"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Jarraitu jakinarazpenak bidaltzen"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Desaktibatu jakinarazpenak"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Aplikazio honen jakinarazpenak erakusten jarraitzea nahi duzu?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Jakinarazi soinurik gabe"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Lehentasunezkoak"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Pantailaren goialdeko goitibeherako barran bakarrik erakusten ditu jakinarazpenak, arretarik gal ez dezazun. Beti isilik."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Lehentasunik ez duten jakinarazpenak erakusten ditu. Beti isilik."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Lehentasunik ez duten jakinarazpenak erakusten ditu. Beti isilik."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Lehentasunik ez duten jakinarazpenak erakusten ditu. Beti isilik."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Arretari dei egiten dio soinua eginda eta egoera-barran ikonoa erakutsita. Pantaila blokeatua agertzen da."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Isila"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Ohartarazlea"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Ez du egiten soinu edo dardararik, arretarik gal ez dezazun."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Arreta erakartzen du soinua eta dardara eginda."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Jakinarazpen horiek ezin dira aldatu."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Jakinarazpen talde hau ezin da konfiguratu hemen"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Proxy bidezko jakinarazpena"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Baimendu"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Ukatu"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Sakatu bateria-aurrezlea noiz aktibatu antolatzeko"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Aktibatu aurrezlea bateria agortzeko arriskua dagoenean"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Ez"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Bateria-aurrezlea aktibatu da"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Bateria-aurrezlea automatikoki aktibatuko da bateriaren %% <xliff:g id="PERCENTAGE">%d</xliff:g> gelditzen denean."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Eraman behealdera, ezkerretara"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Eraman behealdera, eskuinetara"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Baztertu"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index e161b77..e405afe 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"اشکالزدایی USB مجاز نیست"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"کاربری که درحال حاضر در این دستگاه وارد سیستم شده است نمیتواند اشکالزدایی USB را روشن کند. برای استفاده از این قابلیت، به کاربر اصلی تغییر وضعیت دهید."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"درگاه USB غیرفعال شده است"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"برای محافظت از دستگاهتان دربرابر مایعات یا خاکروبه، درگاه USB غیرفعال شده است و هیچکدام از لوازم جانبی را شناسایی نخواهد کرد.\n\nهرزمان که استفاده از درگاه USB امکانپذیر باشد، به شما اطلاع داده میشود."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"درگاه USB برای تشخیص شارژرها و لوازم جانبی فعال شد"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"فعال کردن USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"بیشتر بدانید"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"بزرگنمایی برای پر کردن صفحه"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"گسترده کردن برای پر کردن صفحه"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"عکس صفحهنمایش"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"دوباره نشان داده نشود"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"پاک کردن همه موارد"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"مدیریت"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"اعلانهای بیصدا"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"پاک کردن همه اعلانهای بیصدا"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"اعلانها توسط «مزاحم نشوید» موقتاً متوقف شدند"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"اکنون شروع شود"</string>
<string name="empty_shade_text" msgid="708135716272867002">"اعلانی موجود نیست"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"مسدود کردن"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"همچنان نشان داده شود"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"کوچک کردن"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"بیصدا"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"بیصدا بماند"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"هشدار دادن"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"همچنان اطلاع داده شود"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"خاموش کردن اعلانها"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"نمایش اعلان از این برنامه ادامه یابد؟"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"آرام"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"دراولویت"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"با فقط نمایش اعلان در کشوی اعلانهای پایینکش، به شما کمک میکند تمرکزتان را ازدست ندهید همیشه بیصدا."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"اعلانهای با اولویت پایین را نشان میدهد. همیشه بیصدا."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"اعلانهای با اولویت پایین را نشان میدهد. همیشه بیصدا."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"اعلانهای با اولویت پایین را نشان میدهد. همیشه بیصدا."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"با صدا و نماد نوار وضعیت توجه شما را جلب میکند. در صفحه قفل نشان داده میشود."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"بیصدا"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"هشدار دادن"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"به شما کمک میکند بدون صدا یا لرزش تمرکز کنید."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"با صدا یا لرزش توجه شما را جلب میکند."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"این اعلانها قابل اصلاح نیستند."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"نمیتوانید این گروه اعلانها را در اینجا پیکربندی کنید"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"اعلانهای دارای پراکسی"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"مجاز"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"رد کردن"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"برای زمانبندی «بهینهسازی باتری» ضربه بزنید"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"وقتی باتری روبهاتمام است، بهینهسازی باتری را روشن کنید"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"نه متشکرم"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"زمانبندی «بهینهسازی باتری» روشن شد"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"وقتی شارژ باتری به زیر <xliff:g id="PERCENTAGE">%d</xliff:g>%% برسد، «بهینهسازی باتری» بهطور خودکار روشن میشود."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"انتقال به پایین سمت راست"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"انتقال به پایین سمت چپ"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"رد کردن"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 54d2d93..4e52a4e 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB-vianetsintää ei sallita"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Laitteelle tällä hetkellä kirjautunut käyttäjä ei voi ottaa USB-vianetsintää käyttöön. Vaihda käyttäjäksi ensisijainen käyttäjä, jotta voit käyttää tätä ominaisuutta."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB-portti poistettu käytöstä"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Laitteen suojaamiseksi nesteiltä ja lialta USB-portti on poistettu käytöstä, eikä se havaitse lisävarusteita.\n\nSaat ilmoituksen, kun USB-porttia voi taas käyttää."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB-portti on käytössä ja voi havaita latureita sekä lisävarusteita"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Ota USB käyttöön"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Lue lisää"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoomaa koko näyttöön"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Venytä koko näyttöön"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Kuvakaappaus"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Älä näytä uudelleen"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Poista kaikki"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Muuta asetuksia"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Hiljaiset ilmoitukset"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Tyhjennä kaikki hiljaiset ilmoitukset"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Älä häiritse ‑tila keskeytti ilmoitukset"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Aloita nyt"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Ei ilmoituksia"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Estä"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Jatka näyttämistä"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Pienennä"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Äänetön"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Jatka äänettömyyttä"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Hälyttää"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Jatka ilmoituksista hälyttämistä"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Poista ilmoitukset käytöstä"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Jatketaanko ilmoitusten näyttämistä tästä sovelluksesta?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Varovainen"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Priorisoitu"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Keskittyminen on helpompaa, sillä ilmoitukset näkyvät vain alas vedettävällä alueella. Aina äänetön."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Näytetään priorisoitujen ilmoitusten alapuolella. Aina äänetön."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Näytetään priorisoitujen ilmoitusten alapuolella. Aina äänetön."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Näytetään priorisoitujen ilmoitusten alapuolella. Aina äänetön."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Kiinnittää huomion äänellä ja tilapalkin kuvakkeella. Näkyy lukitusnäytöllä."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Äänetön"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Hälyttää"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Keskittyminen on helpompaa ilman ääntä tai värinää."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Kiinnittää huomion ilman ääntä tai värinää."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Näitä ilmoituksia ei voi muokata"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Tätä ilmoitusryhmää ei voi määrittää tässä"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Välitetty ilmoitus"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Salli"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Estä"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Ajoita virransäästö napauttamalla"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Ota käyttöön, jos akku todennäköisesti loppuu"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Ei kiitos"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Virransäästön ajoitus otettu käyttöön"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Virransäästö käynnistyy automaattisesti, kun akun lataustaso on alle <xliff:g id="PERCENTAGE">%d</xliff:g> %%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Siirrä vasempaan alareunaan"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Siirrä oikeaan alareunaan"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Ohita"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index a904d30..4f6c1b5 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Débogage USB non autorisé"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"L\'utilisateur actuellement connecté sur cet appareil ne peut pas activer le débogage USB. Pour utiliser cette fonctionnalité, l\'utilisateur principal doit se connecter."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Le port USB a été désactivé"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Pour protéger votre appareil des liquides et des débris, le port USB est désactivé et ne pourra pas détecter les accessoires.\n\nVous verrez une notification lorsque vous pourrez utiliser le port USB à nouveau."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Le port USB a été activé afin de détecté les chargeurs et les accessoires"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Activer l\'USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"En savoir plus"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoomer pour remplir l\'écran"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Étirer pour remplir l\'écran"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Capture d\'écran"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ne plus afficher"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Tout effacer"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Gérer"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Notifications silencieuses"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Effacer toutes les notifications silencieuses"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Les notifications sont suspendues par le mode Ne pas déranger"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Commencer"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Aucune notification"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Bloquer"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Continuer à afficher"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Réduire"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Mode silencieux"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Continuer d\'util. mode silencieux"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Alertes"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Continuer d\'envoyer des alertes"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Désactiver les notifications"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuer à afficher les notifications de cette application?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Discrètes"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Priorisées"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Vous aide à vous concentrer en affichant seulement les notifications dans le volet déroulant. Toujours silencieux."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"S\'affiche sous les notifications prioritaires. Toujours silencieux."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"S\'affiche sous les notifications prioritaires. Toujours silencieux."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"S\'affiche sous les notifications prioritaires. Toujours silencieux."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Attire votre attention à l\'aide de sons et d\'une icône dans la barre d\'état. S\'affiche sur l\'écran de verrouillage."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Mode silencieux"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Alertes"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Vous aider à vous concentrer, sans son ni vibration."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Attire votre attention à l\'aide de sons et de vibrations."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Ces notifications ne peuvent pas être modifiées"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Ce groupe de notifications ne peut pas être configuré ici"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Notification par mandataire"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Autoriser"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Refuser"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Toucher pour activer la fonction Économie d\'énergie"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Activer si la pile est susceptible de s\'épuiser totalement"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Non merci"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"La fonction Économie d\'énergie est activée"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"La fonction Économie d\'énergie s\'activera automatiquement une fois que la pile sera en dessous de <xliff:g id="PERCENTAGE">%d</xliff:g> %%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Déplacer dans coin inf. gauche"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Déplacer dans coin inf. droit"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Fermer"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 23f601c..95cd5b6 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Débogage USB non autorisé"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"L\'utilisateur actuellement connecté sur cet appareil ne peut pas activer le débogage USB. Pour utiliser cette fonctionnalité, l\'utilisateur principal doit se connecter."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Port USB désactivé"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Pour protéger votre appareil des liquides et des saletés, le port USB est désactivé et ne détecte plus les accessoires.\n\nVous recevrez une notification lorsque vous pourrez de nouveau utiliser le port USB."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Port USB activé pour détecter les chargeurs et les accessoires"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Activer le port USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"En savoir plus"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoomer pour remplir l\'écran"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Étirer pour remplir l\'écran"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Capture"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ne plus afficher"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Tout effacer"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Gérer"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Notifications silencieuses"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Effacer toutes les notifications silencieuses"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notifications suspendues par le mode Ne pas déranger"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Commencer"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Aucune notification"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Bloquer"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Continuer d\'afficher les notifications"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Réduire"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Mode silencieux"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Notifications silencieuses"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Alertes"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Continuer de m\'avertir"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Désactiver les notifications"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuer d\'afficher les notifications de cette application ?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Discret"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioritaire"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Vous aide à vous concentrer en affichant les notifications seulement dans le volet déroulant. Toujours silencieux."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"S\'affiche sous les notifications prioritaires. Toujours silencieux."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"S\'affiche sous les notifications prioritaires. Toujours silencieux."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"S\'affiche sous les notifications prioritaires. Toujours silencieux."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Attire votre attention à l\'aide d\'un son et d\'une icône dans la barre d\'état. S\'affiche sur l\'écran de verrouillage."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Silencieux"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Alertes"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Sans sons ni vibrations, vous aide à vous concentrer."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Attire votre attention à l\'aide de sons ou de vibrations."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Impossible de modifier ces notifications."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Vous ne pouvez pas configurer ce groupe de notifications ici"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Notification de proxy"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Autoriser"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Refuser"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Appuyez ici pour programmer l\'économiseur de batterie"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Activer l\'économiseur de batterie si l\'autonomie restante risque d\'être insuffisante"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Non, merci"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Programmation de l\'économiseur de batterie activée"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"L\'économiseur de batterie s\'active automatiquement lorsque l\'autonomie de la batterie est inférieure à <xliff:g id="PERCENTAGE">%d</xliff:g> %%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Déplacer en bas à gauche"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Déplacer en bas à droite"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Ignorer"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index fcb82e6..cce17d6 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Non se permite a depuración por USB"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"O usuario coa sesión iniciada actualmente neste dispositivo non pode activar a depuración por USB. Para utilizar esta función, cambia ao usuario principal."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"O porto USB está desactivado"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Para protexer o dispositivo de líquidos ou residuos, desactivouse o porto USB e non detectará ningún accesorio.\n\nRecibirás unha notificación cando o poidas utilizar de novo."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Activouse o porto USB para detectar cargadores e accesorios"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Activar USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Máis información"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Ampliar ata ocupar todo"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Estirar ata ocupar todo"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Capt. pantalla"</string>
@@ -214,7 +212,7 @@
<!-- no translation found for accessibility_work_mode (702887484664647430) -->
<skip />
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificación rexeitada"</string>
- <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Sombra de notificación"</string>
+ <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Panel despregable"</string>
<string name="accessibility_desc_quick_settings" msgid="6186378411582437046">"Configuración rápida"</string>
<string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"Pantalla de bloqueo."</string>
<string name="accessibility_desc_settings" msgid="3417884241751434521">"Configuración"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Non mostrar outra vez"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Eliminar todas"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Xestionar"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Notificacións silenciadas"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Borra todas as notificacións silenciadas"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"O modo Non molestar puxo en pausa as notificacións"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Iniciar agora"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Non hai notificacións"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Bloquear"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Continuar mostrando notificacións"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizar"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Silencio"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Notificacións silenciosas"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Alertando"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Continuar recibindo notificacións"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Desactivar notificacións"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Queres seguir mostrando as notificacións desta aplicación?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Discretas"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioritarias"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Permíteche centrarte con notificacións só no panel despregable. Sempre en silencio."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Mostra as notificacións con prioridade baixa. Sempre en silencio."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Mostra as notificacións con prioridade baixa. Sempre en silencio."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Mostra as notificacións con prioridade baixa. Sempre en silencio."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Chama a túa atención cun son e cunha icona na barra de estado. Móstrase na pantalla de bloqueo."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Silenciosas"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Con alertas"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Axúdache a centrarte sen son nin vibración."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Chama a túa atención con son ou vibración."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Estas notificacións non se poden modificar."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Aquí non se pode configurar este grupo de notificacións"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Notificación mediante proxy"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Permitir"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Denegar"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Tocar para programar a función Aforro de batería"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Activa a función se prevés que a batería pode esgotarse"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Non, grazas"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Activouse o programa da función Aforro de batería"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Activarase automaticamente a función Aforro de batería en canto o nivel de carga sexa inferior ao <xliff:g id="PERCENTAGE">%d</xliff:g> %%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Mover á parte infer. esquerda"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Mover á parte inferior dereita"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Ignorar"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 7b2c6d3..53107f1 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB ડીબગિંગની મંજૂરી નથી"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"હાલમાં આ ઉપકરણમાં સાઇન ઇન થયેલ વપરાશકર્તા USB ડિબગીંગ ચાલુ કરી શકતા નથી. આ સુવિધાનો ઉપયોગ કરવા માટે પ્રાથમિક વપરાશકર્તા પર સ્વિચ કરો."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB પોર્ટ બંધ કરવામાં આવ્યો છે"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"પ્રવાહી અથવા ધૂળથી તમારા ડિવાઇસનું રક્ષણ કરવા માટે, USB પોર્ટ બંધ કરવામાં આવ્યો છે અને કોઈ ઍક્સેસરી શોધશે નહીં.\n\nજ્યારે ફરીથી USB પોર્ટને ઉપયોગમાં લેવાનું સુરક્ષિત હશે ત્યારે તમને નોટિફિકેશન આપવામાં આવશે."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"ચાર્જર અને ઍક્સેસરીની ઓળખ માટે USB પોર્ટ ચાલુ કર્યું"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB ચાલુ કરો"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"વધુ જાણો"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"સ્ક્રીન ભરવા માટે ઝૂમ કરો"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"સ્ક્રીન ભરવા માટે ખેંચો"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"સ્ક્રીનશૉટ"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"ફરીથી બતાવશો નહીં"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"બધુ સાફ કરો"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"મેનેજ કરો"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"સાઇલન્ટ નોટિફિકેશન"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"બધા સાઇલન્ટ નોટિફિકેશન સાફ કરો"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"ખલેલ પાડશો નહીં દ્વારા થોભાવેલ નોટિફિકેશન"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"હવે પ્રારંભ કરો"</string>
<string name="empty_shade_text" msgid="708135716272867002">"કોઈ સૂચનાઓ નથી"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"બ્લૉક કરો"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"બતાવવાનું ચાલુ રાખો"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"નાનું કરો"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"સાઇલન્ટ"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"સાઇલન્ટ મોડ ચાલુ રાખો"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"અલર્ટ કરતા રહો"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"અલર્ટ કરવાનું ચાલુ રાખો"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"નોટિફિકેશન બંધ કરો"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"આ ઍપમાંથી નોટિફિકેશન બતાવવાનું ચાલુ રાખીએ?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"સામાન્ય નોટિફિકેશન"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"મહત્ત્વના નોટિફિકેશન"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"નીચે દેખાતા શેડમાં માત્ર નોટિફિકેશન પર ફોકસ કરવામાં તમને સહાય કરે છે. હંમેશાં સાઇલન્ટ."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"પ્રાધાન્યતાવાળા નોટિફિકેશન નીચે બતાવે છે. હંમેશાં સાઇલન્ટ."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"પ્રાધાન્યતાવાળા નોટિફિકેશન નીચે બતાવે છે. હંમેશાં સાઇલન્ટ."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"પ્રાધાન્યતાવાળા નોટિફિકેશન નીચે બતાવે છે. હંમેશાં સાઇલન્ટ."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"સાઉન્ડ અને સ્ટેટસ બાર આઇકન તમારું ધ્યાન દોરે છે. લૉક સ્ક્રીન પર બતાવે છે."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"સાઇલન્ટ"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"અલર્ટ કરવાનું ચાલુ રાખો"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"તમને સાઉન્ડ અથવા વાઇબ્રેશન વિના ફોકસ કરવામાં સહાય કરે છે."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"સાઉન્ડ અથવા વાઇબ્રેશન વિના તમારું ધ્યાન દોરે છે."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"આ નોટિફિકેશનમાં કોઈ ફેરફાર થઈ શકશે નહીં."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"નોટિફિકેશનના આ ગ્રૂપની ગોઠવણી અહીં કરી શકાશે નહીં"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"પ્રૉક્સી નોટિફિકેશન"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"મંજૂરી આપો"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"નકારો"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"બૅટરી સેવર શેડ્યૂલ કરવા માટે ટૅપ કરો"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"જ્યારે બૅટરી સંભવિત રૂપે પૂરી થવામાં હોય ત્યારે બૅટરી સેવર ચાલુ કરો"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"ના, આભાર"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"બૅટરી સેવર શેડ્યૂલ ચાલુ થયું"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"બૅટરીનું સ્તર એકવાર <xliff:g id="PERCENTAGE">%d</xliff:g>%% કરતાં ઓછું થાય તે પછી બૅટરી સેવર ઑટોમૅટિક રીતે ચાલુ થશે."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"નીચે ડાબે ખસેડો"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"નીચે જમણે ખસેડો"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"છોડી દો"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 941ca05..92f3725 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB डीबगिंग की अनुमति नहीं है"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"अभी इस डिवाइस में जिस उपयोगकर्ता ने साइन इन किया है, वो USB डीबगिंग चालू नहीं कर सकता. इस सुविधा का इस्तेमाल करने के लिए, प्राथमिक उपयोगकर्ता में बदलें."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"यूएसबी पोर्ट बंद है"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"तरल चीज़ या कचरे से आपके डिवाइस की सुरक्षा करने के लिए, यूएसबी पोर्ट को बंद कर दिया गया है. साथ ही, इससे किसी भी एक्सेसरी की पहचान नहीं की जा सकेगी.\n\nयूएसबी पोर्ट का दोबारा इस्तेमाल करना सुरक्षित होने पर आपको सूचित किया जाएगा."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"चार्जर और एक्सेसरी पहचानने के लिए यूएसबी पोर्ट को चालू कर दिया गया है"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"यूएसबी चालू करें"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"ज़्यादा जानें"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"स्क्रीन भरने के लिए ज़ूम करें"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"स्क्रीन भरने के लिए खींचें"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"स्क्रीनशॉट"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"फिर से न दिखाएं"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"सभी को हटाएं"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"प्रबंधित करें"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"बिना आवाज़ या वाइब्रेशन वाली सूचनाएं"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"बिना आवाज़ की सभी सूचनाएं हटाएं"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"\'परेशान न करें\' सुविधा के ज़रिए कुछ समय के लिए सूचनाएं दिखाना रोक दिया गया है"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"अब शुरू करें"</string>
<string name="empty_shade_text" msgid="708135716272867002">"कोई सूचना नहीं मिली"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"ब्लॉक करें"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"दिखाना जारी रखें"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"सूचनाएं छोटी करें"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"बिना आवाज़ के सूचनाएं दिखाएं"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"साइलेंट मोड में सूचनाएं पाएं"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"चेतावनी देना"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"सूचना देना जारी रखें"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"सूचनाएं बंद करें"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"इस ऐप्लिकेशन से जुड़ी सूचनाएं दिखाना जारी रखें?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"बिना आवाज़ के"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"ज़रूरी"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"सिर्फ़ नीचे खिंचने वाली सूची में सूचनाएं दिखती हैं ताकि आप उन पर पूरा ध्यान दे सकें. दिखते समय आवाज़ और वाइब्रेशन हमेशा बंद रहते हैं."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"कम ज़रूरी सूचनाएं दिखती हैं. दिखते समय आवाज़ और वाइब्रेशन हमेशा बंद रहते हैं."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"कम ज़रूरी सूचनाएं दिखती हैं. दिखते समय आवाज़ और वाइब्रेशन हमेशा बंद रहते हैं."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"कम ज़रूरी सूचनाएं दिखती हैं. दिखते समय आवाज़ और वाइब्रेशन हमेशा बंद रहते हैं."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"आवाज़ और स्टेटस बार के आइकॉन की मदद से सूचनाओं पर आपका ध्यान खिंचता है. लॉक स्क्रीन पर दिखाई देता है."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"आवाज़ के बिना सूचनाएं दिखाएं"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"चेतावनी वाली सूचनाएं"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"आवाज़ या वाइब्रेशन न होने की वजह से आप काम में ध्यान लगा पाते हैं."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"आवाज़ या वाइब्रेशन होने की वजह से आपका ध्यान सूचनाओं पर जाता है."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"ये सूचनाएं नहीं बदली जा सकती हैं."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"सूचनाओं के इस समूह को यहां कॉन्फ़िगर नहीं किया जा सकता"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"प्रॉक्सी सूचना"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"मंज़ूरी दें"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"नामंज़ूर करें"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"बैटरी सेवर शेड्यूल करने के लिए टैप करें"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"जब बैटरी खत्म होने वाली हो तब \'बैटरी सेवर\' चालू करें"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"जी नहीं, शुक्रिया"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"शेड्यूल किया गया बैटरी सेवर चालू हो गया"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"बैटरी के <xliff:g id="PERCENTAGE">%d</xliff:g>%% से कम होने पर बैटरी सेवर अपने आप चालू हो जाएगा."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"बाईं ओर सबसे नीचे ले जाएं"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"सबसे नीचे दाईं ओर ले जाएं"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"खारिज करें"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index ba0d1fb..04200c1 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Otklanjanje pogrešaka putem USB-a nije dopušteno"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Korisnik koji je trenutačno prijavljen na ovaj uređaj ne može uključiti otklanjanje pogrešaka putem USB-a. Da biste upotrebljavali tu značajku, prijeđite na primarnog korisnika."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Onemogućen je USB priključak"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Radi zaštite uređaja od tekućine ili prljavštine USB priključak onemogućen je i neće otkrivati pribor.\n\nPrimit ćete obavijest kad upotreba USB priključka ponovo bude sigurna."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB priključak omogućen za otkrivanje punjača i opreme"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Omogući USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Saznajte više"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zumiraj i ispuni zaslon"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Rastegni i ispuni zaslon"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Snimka zaslona"</string>
@@ -461,10 +459,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ne prikazuj ponovo"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Izbriši sve"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Upravljajte"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Bešumne obavijesti"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Izbriši sve bešumne obavijesti"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Značajka Ne uznemiravaj pauzirala je Obavijesti"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Započni sad"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Nema obavijesti"</string>
@@ -648,21 +644,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Blokiraj"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Nastavi prikazivati"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimiziraj"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Bešumno"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Nastavi tiho"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Obavještavanje"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Nastavi obavještavati"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Isključi obavijesti"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Želite li da se obavijesti te aplikacije nastave prikazivati?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Neupadljivo"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioritetno"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Pomaže vam da se usredotočite prikazujući obavijesti samo na zaslonu obavijesti. Uvijek bešumno."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Prikazuje se ispod prioritetnih obavijesti. Uvijek bešumno."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Prikazuje se ispod prioritetnih obavijesti. Uvijek bešumno."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Prikazuje se ispod prioritetnih obavijesti. Uvijek bešumno."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Privlači vašu pažnju zvučnim signalom i ikonom na traci statusa. Prikazuje se na zaključanom zaslonu."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Bešumno"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Upozoravanje"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Pomaže vam da se usredotočite bez zvučnih signala i vibracija."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Privlači vašu pažnju zvučnim signalima ili vibracijama."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Te se obavijesti ne mogu izmijeniti."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Ta se grupa obavijesti ne može konfigurirati ovdje"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Obavijest poslana putem proxyja"</string>
@@ -913,8 +904,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Dopusti"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Odbij"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Dodirnite za zakazivanje štednje baterije"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Uključite kad bi se baterija mogla isprazniti"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Ne, hvala"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Uključen je raspored štednje baterije"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Štednja baterije uključit će se automatski kad razina baterije padne ispod <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -947,4 +937,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Premjesti u donji lijevi kut"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Premjestite u donji desni kut"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Odbaci"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 941e229..cd5de1a 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Az USB hibakeresése nem engedélyezett"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Az eszközre jelenleg bejelentkezett felhasználó nem engedélyezheti az USB-hibakeresést. A funkció használatához váltson az elsődleges felhasználóra."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB-port letiltva"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Az eszköz folyadéktól és szennyeződésektől való megóvása érdekében az USB-portot letiltottuk, így az nem észleli a kiegészítőket.\n\nÉrtesítést küldünk, amikor ismét rendben használhatja az USB-portot."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Az USB-csatlakozó számára engedélyezve van a töltők és más tartozékok észlelése"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB engedélyezése"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Részletek"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Nagyítás a kitöltéshez"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Nyújtás kitöltéshez"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Képernyőkép"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ne jelenjen meg többé"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Az összes törlése"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Kezelés"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Néma értesítések"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Az összes néma értesítés törlése"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Ne zavarjanak funkcióval szüneteltetett értesítések"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Indítás most"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Nincs értesítés"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Tiltás"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Megjelenítés továbbra is"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Kis méret"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Néma"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Néma megjelenítés"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Értesítések"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Értesítések folytatása"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Az értesítések kikapcsolása"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Továbbra is megjelenjenek az alkalmazás értesítései?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Diszkrét"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioritásos"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Segítségével a fontos dolgokra koncentrálhat, az értesítések csak a lehúzható értesítési felületen jelennek meg. Mindig némítva van."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Az elsőbbségi értesítések alatt jelenik meg. Mindig némítva van."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Az elsőbbségi értesítések alatt jelenik meg. Mindig némítva van."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Az elsőbbségi értesítések alatt jelenik meg. Mindig némítva van."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Figyelemfelkeltő a hangjelzésnek és az állapotsávon megjelenő ikonnak köszönhetően. Megjelenik a lezárási képernyőn."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Néma"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Figyelemfelkeltő"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Hangjelzés és rezgés nélkül segít a koncentrálásban."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Figyelemfelkeltő a hangjelzésnek és rezgésnek köszönhetően."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Ezeket az értesítéseket nem lehet módosítani."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Az értesítések jelen csoportját itt nem lehet beállítani"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Továbbított értesítés"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Engedélyezés"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Elutasítás"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Koppintson az akkumulátorkímélő mód ütemezéséhez"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Kapcsolja be, ha az akkumulátor hamarosan lemerül"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Nem"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Akkumulátorkímélő mód ütemezése bekapcsolva"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Az akkumulátorkímélő mód automatikusan bekapcsol, ha az akkumulátor töltöttsége <xliff:g id="PERCENTAGE">%d</xliff:g>%% alá esik."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Áthelyezés le és balra"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Áthelyezés le és jobbra"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Elvetés"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 9ebd2d6..be53647 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB վրիպազերծումը արգելված է"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Ընթացիկ հաշվի օգտատերը չի կարող միացնել USB վրիպազերծումը: Այս գործառույթը միացնելու համար մուտք գործեք հիմնական օգտատիրոջ հաշվով:"</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB միացքն անջատված է"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"USB միացքն անջատվել է, որպեսզի ձեր սարքը չթրջվի կամ չաղտոտվի: Այժմ USB միացքի միջոցով հնարավոր չէ միացնել այլ սարքեր:\n\nԴուք ծանուցում կստանաք, երբ այն նորից անվտանգ լինի օգտագործել:"</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB միացքը միացվել է՝ լիցքավորիչներն ու լրասարքերը հայտնաբերելու համար"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Միացնել USB-ն"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Իմանալ ավելին"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Խոշորացնել` էկրանը լցնելու համար"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Ձգել` էկրանը լցնելու համար"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Սքրինշոթ"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Այլևս ցույց չտալ"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Մաքրել բոլորը"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Կառավարել"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Անձայն ծանուցումներ"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Ջնջել բոլոր անձայն ծանուցումները"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Ծանուցումները չեն ցուցադրվի «Չանհանգստացնել» ռեժիմում"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Սկսել հիմա"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Ծանուցումներ չկան"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Արգելափակել"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Ցուցադրել"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Ծալել"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Անձայն"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Չմիացնել ձայնը"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Ծանուցել"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Ծանուցել"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Անջատել ծանուցումները"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Ցուցադրե՞լ ծանուցումներ այս հավելվածից։"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Անձայն"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Առաջնահերթ"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Ծանուցումները ցուցադրվում են միայն իջնող վահանակի վրա, որպեսզի դուք չշեղվեք ձեր աշխատանքից: Միշտ անձայն:"</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Ցուցադրվում են կարևոր ծանուցումների տակ: Միշտ անձայն:"</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Ցուցադրվում են կարևոր ծանուցումների տակ: Միշտ անձայն:"</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Ցուցադրվում են կարևոր ծանուցումների տակ: Միշտ անձայն:"</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Ծանուցումները ձեր ուշադրությունն են գրավում ազդանշանով և կարգավիճակի գոտում ցուցադրվող պատկերակով: Ցուցադրվում են կողպէկրանին:"</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Անձայն"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Ծանուցումներ"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Ծանուցումները գալիս են առանց ձայնի և թրթռոցի։"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Ծանուցումները գալիս են ձայնով կամ թրթռոցով։"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Այս ծանուցումները չեն կարող փոփոխվել:"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Ծանուցումների տվյալ խումբը հնարավոր չէ կարգավորել այստեղ"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Ծանուցումն ուղարկվել է պրոքսի սերվերի միջոցով"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Թույլատրել"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Մերժել"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Հպեք՝ մարտկոցի տնտեսման ռեժիմը կարգավորելու համար"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Միացնել էներգախնայումը, երբ մարտկոցի լիցքը գրեթե սպառված է"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Ոչ"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Մարտկոցի տնտեսման ռեժիմին ավտոմատ անցումը միացված է"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Մարտկոցի տնտեսման ռեժիմն ավտոմատ կմիանա, երբ մարտկոցի լիցքը <xliff:g id="PERCENTAGE">%d</xliff:g>%%-ից պակասի:"</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Տեղափոխել ներքև՝ ձախ"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Տեղափոխել ներքև՝ աջ"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Փակել"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index bf5d3a1..671f5f74 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -62,7 +62,7 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Debug USB tidak diizinkan"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Pengguna yang sedang login ke perangkat ini tidak dapat mengaktifkan proses debug USB. Beralihlah ke pengguna utama untuk menggunakan fitur ini."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Port USB dinonaktifkan"</string>
- <string name="usb_contaminant_message" msgid="7379089091591609111">"Untuk melindungi perangkat dari cairan atau kotoran, port USB dinonaktifkan dan tidak akan mendeteksi aksesori apa pun.\n\nAnda akan diberi tahu jika sudah diperbolehkan menggunakan port USB kembali."</string>
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Untuk melindungi perangkat dari cairan atau kotoran, port USB dinonaktifkan dan tidak akan mendeteksi aksesori apa pun.\n\nAnda akan diberi tahu jika port USB sudah dapat digunakan kembali."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Port USB diaktifkan untuk mendeteksi pengisi daya dan aksesori"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Aktifkan USB"</string>
<string name="learn_more" msgid="5000517160980853569">"Pelajari lebih lanjut"</string>
@@ -647,13 +647,10 @@
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Terus beri tahu"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Nonaktifkan notifikasi"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Terus tampilkan notifikasi dari aplikasi ini?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Senyap"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Diprioritaskan"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Membantu Anda berfokus hanya pada notifikasi di bayangan pull-down. Selalu senyap."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Ditampilkan di bawah notifikasi prioritas. Selalu senyap."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Ditampilkan di bawah notifikasi prioritas. Selalu senyap."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Ditampilkan di bawah notifikasi prioritas. Selalu senyap."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Menarik perhatian Anda dengan suara & ikon status bar. Ditampilkan di layar kunci."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Senyap"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Pemberitahuan"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Membantu Anda tetap fokus tanpa suara atau getaran."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Menarik perhatian Anda dengan suara atau getaran."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Notifikasi ini tidak dapat diubah."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Grup notifikasi ini tidak dapat dikonfigurasi di sini"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Notifikasi proxy"</string>
@@ -935,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Pindahkan ke kiri bawah"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Pindahkan ke kanan bawah"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Tutup"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index b4b9be9..69cc693 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB-villuleit ekki leyfð"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Notandinn sem er skráður inn í þetta tæki núna getur ekki kveikt á USB-villuleit. Til þess að nota þennan eiginleika skaltu skipta yfir í aðalnotandann."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB-tengi gert óvirkt"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Til að vernda tækið fyrir vökva og óhreinindum er USB-tengið óvirkt og mun ekki greina aukabúnað.\n\nÞú færð tilkynningu þegar öruggt er að nota USB-tengið aftur."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Kveikt var á USB-tengi til að greina hleðslutæki og aukabúnað"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Virkja USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Frekari upplýsingar"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Fylla skjá með aðdrætti"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Teygja yfir allan skjáinn"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Skjámynd"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ekki sýna þetta aftur"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Hreinsa allt"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Stjórna"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Þöglar tilkynningar"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Hreinsa allar þöglar tilkynningar"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Hlé gert á tilkynningum þar sem stillt er á „Ónáðið ekki“"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Byrja núna"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Engar tilkynningar"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Loka á"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Sýna áfram"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minnka"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Hljóðlaust"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Áfram hljóðlaust"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Tilkynna"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Halda áfram að gera viðvart"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Slökkva á tilkynningum"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Sýna áfram tilkynningar frá þessu forriti?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Lágstemmdar"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Í forgangi"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Auðveldar þér að einbeita þér með tilkynningum sem birtast aðeins á fellisvæði. Alltaf án hljóðs."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Birtir tilkynningar með lítinn forgang. Alltaf án hljóðs."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Birtir tilkynningar með lítinn forgang. Alltaf án hljóðs."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Birtir tilkynningar með lítinn forgang. Alltaf án hljóðs."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Fangar athygli þína með hljóði og stöðustikutákni. Sést á lásskjánum."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Hljóðlaust"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Viðvörun"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Auðveldar þér að einbeita þér án hljóðs eða titrings."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Fangar athygli þína með hljóði eða titringi."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Ekki er hægt að breyta þessum tilkynningum."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Ekki er hægt að stilla þessar tilkynningar hér"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Staðgengilstilkynning"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Leyfa"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Hafna"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Pikka til að stilla rafhlöðusparnað"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Kveikja þegar rafhlaða er við það að klárast"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Nei, takk"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Kveikt á rafhlöðusparnaði"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Sjálfkrafa verður kveikt á rafhlöðusparnaði þegar hleðsla rafhlöðunnar fer niður fyrir <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Færa neðst til vinstri"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Færðu neðst til hægri"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Hunsa"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 52b915a..dfdf5f6 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -647,13 +647,10 @@
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Continua ad avvisare"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Disattiva notifiche"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuare a ricevere notifiche da questa app?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Silenziose"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Con priorità"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Puoi concentrarti perché le notifiche sono visualizzate solo nell\'area a discesa. Sempre silenziose."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Vengono mostrate le notifiche con priorità bassa. Sempre silenziose."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Vengono mostrate le notifiche con priorità bassa. Sempre silenziose."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Vengono mostrate le notifiche con priorità bassa. Sempre silenziose."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Attirano la tua attenzione con un suono e un\'icona nella barra di stato. Le notifiche sono mostrate nella schermata di blocco."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Modalità silenziosa"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Avvisi"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Favorisce la tua concentrazione grazie all\'assenza di suono o vibrazione."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Richiama la tua attenzione con suono o vibrazione."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Impossibile modificare queste notifiche."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Qui non è possibile configurare questo gruppo di notifiche"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Notifica inviata al proxy"</string>
@@ -935,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Sposta in basso a sinistra"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Sposta in basso a destra"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Ignora"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 684398d7..571b37d 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"לא ניתן לבצע ניפוי באגים ב-USB"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"למשתמש המחובר לחשבון במכשיר הזה אין אפשרות להפעיל ניפוי באגים ב-USB. כדי להשתמש בתכונה הזו יש לעבור אל המשתמש הראשי."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"יציאת ה-USB מושבתת"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"כדי להגן על המכשיר שלך מנוזלים או חלקיקים, יציאת ה-USB מושבתת ולא מזהה אביזרים כלל.\n\nתתקבל התראה כשניתן יהיה להשתמש ביציאת ה-USB."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"יציאת USB הופעלה לזיהוי מטענים ואביזרים"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"הפעלת USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"מידע נוסף"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"הגדל תצוגה כדי למלא את המסך"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"מתח כדי למלא את המסך"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"צילום מסך"</string>
@@ -464,10 +462,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"אל תציג שוב"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"נקה הכל"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"ניהול"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"התראות שקטות"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"ניקוי כל ההתראות השקטות"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"התראות הושהו על ידי מצב \'נא לא להפריע\'"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"התחל כעת"</string>
<string name="empty_shade_text" msgid="708135716272867002">"אין התראות"</string>
@@ -651,21 +647,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"חסימה"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"כן, המשיכו"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"מזעור"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"שקטה"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"בשקט"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"שליחת התראות"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"המשך שליחת התראות"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"השבתת ההתראות"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"שנמשיך להציג לך התראות מהאפליקציה הזאת?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"ברמה מתונה"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"בעדיפות"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"עוזרת לריכוז באמצעות הצגת התראות בלוח ההתראות הנפתח בלבד. תמיד שקטה."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"יופיעו התראות בעדיפות נמוכה. תמיד בשקט."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"יופיעו התראות בעדיפות נמוכה. תמיד בשקט."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"יופיעו התראות בעדיפות נמוכה. תמיד בשקט."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"מעוררת תשומת לב באמצעות צלילים וסמל בשורת הסטטוס. מוצגת במסך הנעילה."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"שקט"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"שליחת התראות"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"עוזרת להתרכז ללא צלילים או רטט."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"מעוררת תשומת לב באמצעות צלילים או רטט."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"לא ניתן לשנות את ההתראות האלה."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"לא ניתן להגדיר כאן את קבוצת ההתראות הזו"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"התראה דרך שרת proxy"</string>
@@ -918,8 +909,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"אני רוצה לאשר"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"אני לא מרשה"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"יש להקיש כדי לתזמן את מצב החיסכון בסוללה"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"מומלץ להפעיל את התכונה כשיש סבירות גבוהה שהסוללה תתרוקן"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"לא תודה"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"הופעל תזמון של חיסכון בסוללה"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"מצב חיסכון בסוללה יופעל באופן אוטומטי כשרמת טעינת הסוללה תהיה נמוכה מ-<xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -952,4 +942,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"העברה לפינה השמאלית התחתונה"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"העברה לפינה הימנית התחתונה"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"סגירה"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 39a8419..b14d37c 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USBデバッグは許可されていません"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"このデバイスに現在ログインしているユーザーでは、USB デバッグを ON にすることはできません。この機能を使用するには、メインユーザーに切り替えてください。"</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB ポート無効"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"液体やゴミからデバイスを保護するため、USB ポートは無効になっています。アクセサリの検出は行われません。\n\nUSB ポートを再び安全に使用できるようになりましたらお知らせします。"</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB ポートが有効になり、充電器やアクセサリを検出できるようになりました"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB を有効にする"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"詳細"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"画面サイズに合わせて拡大"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"画面サイズに合わせて拡大"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"画面の保存"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"次回から表示しない"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"すべて消去"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"管理"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"サイレント通知"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"サイレント通知がすべて消去されます"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"サイレント モードにより通知は一時停止中です"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"今すぐ開始"</string>
<string name="empty_shade_text" msgid="708135716272867002">"通知はありません"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"ブロック"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"今後も表示する"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"最小化"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"サイレント"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"音なしで通知"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"アラートを受け取る"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"今後もアラートを受け取る"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"通知を OFF にする"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"このアプリからの通知を今後も表示しますか?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"通知音なし"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"優先"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"プルダウン シェードでのみ通知を確認できます。常に通知音は鳴りません。"</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"優先度の低い通知を表示します。常に通知音は鳴りません。"</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"優先度の低い通知を表示します。常に通知音は鳴りません。"</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"優先度の低い通知を表示します。常に通知音は鳴りません。"</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"通知音とステータスバーのアイコンで注意を促します。ロック画面に表示されます。"</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"サイレント"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"アラートを受け取る"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"音やバイブレーションが作動しないため、通知に煩わされずに済みます。"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"音やバイブレーションで通知をお知らせします。"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"これらの通知は変更できません。"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"このグループの通知はここでは設定できません"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"代理通知"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"許可"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"拒否"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"タップしてバッテリー セーバーのスケジュールを設定"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"電池切れになる可能性が高くなると有効になります"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"いいえ"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"バッテリー セーバーのスケジュール設定 ON"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"電池が <xliff:g id="PERCENTAGE">%d</xliff:g>%% を下回ると、バッテリー セーバーが自動的に ON になります。"</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"左下に移動"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"右下に移動"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"閉じる"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 0cc5c5e..a3375cb 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB ხარვეზების გამართვა ნებადართული არაა"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ამ მოწყობილობაზე ამჟამად შესულ მომხმარებელს არ შეუძლია USB ხარვეზების გამართვის ფუნქციის ჩართვა. ამ ფუნქციის გამოსაყენებლად, მიუერთდით მთავარ მომხმარებელს."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB პორტი გათიშულია"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"თქვენი მოწყობილობის სითხის ან ნადებისგან დასაცავად, USB პორტი გათიშულია და ვერცერთი აქსესუარის აღმოჩენას ვერ შეძლებს.\n\nთქვენ მიიღებთ შეტყობინებას, როდესაც USB პორტის გამოყენება კვლავ შესაძლებელი იქნება."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB პორტი ჩართულია დამტენებისა და აქსესუარების აღმოსაჩენად"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB-ის ჩართვა"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"შეიტყვეთ მეტი"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"მასშტაბი შეცვალეთ ეკრანის შესავსებად."</string>
<string name="compat_mode_off" msgid="4434467572461327898">"გაწიეთ ეკრანის შესავსებად."</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"ეკრანის ანაბეჭდი"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"აღარ მაჩვენო"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"ყველას გასუფთავება"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"მართვა"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"ჩუმი შეტყობინებები"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"ყველა ჩუმი შეტყობინების გასუფთავება"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"შეტყობინებები დაპაუზდა „არ შემაწუხოთ“ რეჟიმის მეშვეობით"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"დაწყება ახლავე"</string>
<string name="empty_shade_text" msgid="708135716272867002">"შეტყობინებები არ არის."</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"დაბლოკვა"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"ჩვენების გაგრძელება"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"ჩაკეცვა"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"ჩუმი"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"კვლავ ჩუმად ჩვენება"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"გაფრთხილება"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"გაფრთხილების გაგრძელება"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"შეტყობინებების გამორთვა"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"გაგრძელდეს შეტყობინებათა ჩვენება ამ აპიდან?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"მსუბუქი"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"პრიორიტეტული"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"გეხმარებათ იმ შეტყობინებებზე ყურადღების გამახვილებაში, რომლებიც ჩამოსაწევ ფარდაში ჩანს. ყოველთვის ჩუმი."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"აჩვენებს ნაკლებად პრიორიტეტულ შეტყობინებებს. ყოველთვის ჩუმი."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"აჩვენებს ნაკლებად პრიორიტეტულ შეტყობინებებს. ყოველთვის ჩუმი."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"აჩვენებს ნაკლებად პრიორიტეტულ შეტყობინებებს. ყოველთვის ჩუმი."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"იქცევს თქვენს ყურადღებას ხმით & სტატუსის ზოლის ხატულით. ჩნდება ჩაკეტილ ეკრანზე."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"ჩუმი"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"გამაფრთხილებელი"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"გეხმარებათ ფოკუსირებაში ხმის ან ვიბრაციის უქონლობის გამო."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"იპყრობს თქვენს ყურადღებას ხმით ან ვიბრაციით."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"ამ შეტყობინებების შეცვლა შეუძლებელია."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"შეტყობინებების ამ ჯგუფის კონფიგურირება აქ შეუძლებელია"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"პროქსირებული შეტყობინება"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"დაშვება"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"უარყოფა"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"შეეხეთ ბატარეის დამზოგის დასაგეგმად"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"ჩაირთოს, როცა ბატარეა დაცლის პირას არის"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"არა, გმადლობთ"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"ბატარეის დამზოგის განრიგი ჩართულია"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"ბატარეის დამზოგი ავტომატურად ჩაირთვება, როცა ბატარეა ჩამოსცდება <xliff:g id="PERCENTAGE">%d</xliff:g>%%-ს."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"ქვევით და მარცხნივ გადატანა"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"გადაანაცვ. ქვემოთ და მარჯვნივ"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"დახურვა"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index bb14876..5026b53 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB жөндеу рұқсат етілмеген"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Бұл құрылғыға жаңа кірген пайдаланушы USB түзетуін іске қосылмайды. Бұл мүмкіндікті пайдалану үшін негізгі пайдаланушыға ауысыңыз."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB порты өшірілді"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Құрылғыңызға сұйықтық немесе қоқыс кіріп кетпеуі үшін, USB порты өшірілген және ешқандай керек-жарақты анықтамайды.\n\nUSB портын қайта пайдалануға болатын кезде хабарландыру аласыз."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Зарядтағыштар мен аксессуарларды анықтау үшін USB порты қосылды."</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB қосу"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Толығырақ"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Экранды толтыру үшін ұлғайту"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Экранды толтыру үшін созу"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Скриншот"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Қайта көрсетпеу"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Барлығын тазалау"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Басқару"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Дыбыссыз хабарландырулар"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Барлық дыбыссыз хабарландыруларды өшіру"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Хабарландырулар \"Мазаламау\" режимінде кідіртілді"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Қазір бастау"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Хабарландырулар жоқ"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Бөгеу"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Көрсету"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Жасыру"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Дыбыссыз"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Хабарландырулар алғым келмейді"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Ескерту"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Хабарландырулар келе берсін"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Хабарландыруларды өшіру"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Осы қолданбаның хабарландырулары көрсетілсін бе?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Дыбыссыз"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Маңызды"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Ашылмалы панельдегі хабарландырулар ғана көрсетіледі. Үнемі дыбыссыз режимде болады."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Маңыздылығы төмен хабарландыруларды көрсетеді. Үнемі дыбыссыз режимде болады."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Маңыздылығы төмен хабарландыруларды көрсетеді. Үнемі дыбыссыз режимде болады."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Маңыздылығы төмен хабарландыруларды көрсетеді. Үнемі дыбыссыз режимде болады."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Дыбыс және күй жолағы белгішесі арқылы ескертеді. Құлыптаулы экранда көрсетіледі."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Дыбыссыз"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Ескертулер"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Хабарландырулар келгенде, дыбыс шықпайды не дірілдемейді"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Хабарландырулар келгенде, дыбыс шығады не дірілдейді"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Бұл хабарландыруларды өзгерту мүмкін емес."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Мұндай хабарландырулар бұл жерде конфигурацияланбайды."</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Прокси-сервер арқылы жіберілген хабарландыру"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Рұқсат беру"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Тыйым салу"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Түймені түртіп, Battery Saver функциясын реттеңіз"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Батареяның заряды бітуге жақындағанда қосыңыз."</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Жоқ, рақмет"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Battery Saver кестесі қосылды"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Батарея заряды <xliff:g id="PERCENTAGE">%d</xliff:g>%% деңгейінен төмендегенде, Battery Saver автоматты түрде қосылады."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Төменгі сол жаққа жылжыту"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Төменгі оң жаққа жылжыту"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Жабу"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 2c42f6a..9359868 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"មិនអនុញ្ញាតការកែកំហុសតាម USB ទេ"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"អ្នកប្រើដែលបច្ចុប្បន្នបានចូលគណនីនៅលើឧបករណ៍នេះមិនអាចបើកការកែកំហុស USB បានទេ។ ដើម្បីប្រើមុខងារនេះ សូមប្តូរទៅអ្នកប្រើចម្បង។"</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"បានបិទរន្ធ USB"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"ដើម្បីការពារឧបករណ៍របស់អ្នកកុំឱ្យចូលទឹក ឬកម្ទេចផ្សេងៗ រន្ធ USB ត្រូវបានបិទ ហើយនឹងមិនស្គាល់គ្រឿងបរិក្ខារនោះទេ។\n\nអ្នកនឹងទទួលបានការជូនដំណឺង នៅពេលអ្នកអាចប្រើប្រាស់រន្ធ USB ម្ដងទៀត។"</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"បានបើករន្ធ USB ដើម្បីសម្គាល់ឆ្នាំងសាក និងគ្រឿងផ្សេងៗ"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"បើក USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"ស្វែងយល់បន្ថែម"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"ពង្រីកដើម្បីឲ្យពេញអេក្រង់"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"ទាញដើម្បីឲ្យពេញអេក្រង់"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"រូបថតអេក្រង់"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"កុំបង្ហាញម្ដងទៀត"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"សម្អាតទាំងអស់"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"គ្រប់គ្រង"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"ការជូនដំណឹងស្ងាត់"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"សម្អាតការជូនដំណឹងស្ងាត់ទាំងអស់"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"ការជូនដំណឹងបានផ្អាកដោយមុខងារកុំរំខាន"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"ចាប់ផ្ដើមឥឡូវ"</string>
<string name="empty_shade_text" msgid="708135716272867002">"គ្មានការជូនដំណឹង"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"ទប់ស្កាត់"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"បន្តបង្ហាញ"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"បង្រួម"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"ស្ងាត់"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"បន្តបិទសំឡេង"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"ការជូនដំណឹង"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"បន្តជូនដំណឹង"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"បិទការជូនដំណឹង"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"បន្តបង្ហាញការជូនដំណឹងពីកម្មវិធីនេះ?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"ស្ងាត់ៗ"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"ជាអាទិភាព"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"ជួយឱ្យអ្នកផ្ដោតតាមរយៈការជូនដំណឹងតែនៅក្នុងផ្ទាំងជូនដំណឹងធ្លាក់ចុះប៉ុណ្ណោះ។ បិទសំឡេងជានិច្ច។"</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"បង្ហាញការជូនដំណឹងអាទិភាពខាងក្រោម។ បិទសំឡេងជានិច្ច។"</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"បង្ហាញការជូនដំណឹងអាទិភាពខាងក្រោម។ បិទសំឡេងជានិច្ច។"</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"បង្ហាញការជូនដំណឹងអាទិភាពខាងក្រោម។ បិទសំឡេងជានិច្ច។"</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"ធ្វើឱ្យអ្នកចាប់អារម្មណ៍តាមរយៈសំឡេង និងរូបរបារស្ថានភាព។ បង្ហាញនៅលើអេក្រង់ចាក់សោ។"</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"ស្ងាត់"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"បញ្ចេញសំឡេង"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"ជួយឱ្យអ្នកផ្តោតអារម្មណ៍ ដោយមិនឮសំឡេង ឬការញ័រ។"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"ធ្វើឱ្យអ្នកចាប់អារម្មណ៍តាមរយៈសំឡេង ឬការញ័រ។"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"មិនអាចកែប្រែការជូនដំណឹងទាំងនេះបានទេ។"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"មិនអាចកំណត់រចនាសម្ព័ន្ធក្រុមការជូនដំណឹងនេះនៅទីនេះបានទេ"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"ការជូនដំណឹងជាប្រូកស៊ី"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"អនុញ្ញាត"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"បដិសេធ"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"ចុចដើម្បីកំណត់កាលវិភាគកម្មវិធីសន្សំថ្ម"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"បើកនៅពេលថ្មទំនងជាអស់"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"ទេ អរគុណ"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"កាលវិភាគកម្មវិធីសន្សំថ្មបានបើកហើយ"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"កម្មវិធីសន្សំថ្មនឹងបើកដោយស្វ័យប្រវត្តិ នៅពេលថ្មនៅសល់តិចជាង <xliff:g id="PERCENTAGE">%d</xliff:g>%% ។"</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"ផ្លាស់ទីទៅផ្នែកខាងក្រោមខាងឆ្វេង"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"ផ្លាស់ទីទៅផ្នែកខាងក្រោមខាងស្ដាំ"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"ច្រានចោល"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index f14998d..c28b5e2 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -647,13 +647,10 @@
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"ಎಚ್ಚರಿಸುತ್ತಿರಿ"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"ಅಧಿಸೂಚನೆಗಳನ್ನು ಆಫ್ ಮಾಡಿ"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"ಈ ಅಪ್ಲಿಕೇಶನ್ನಿಂದ ಅಧಿಸೂಚನೆಗಳನ್ನು ತೋರಿಸುತ್ತಲೇ ಇರಬೇಕೆ?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"ಸಾಮಾನ್ಯ"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"ಆದ್ಯತೆಗೊಳಿಸಿದ"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"ಪುಲ್-ಡೌನ್ ಶೇಡ್ನಲ್ಲಿರುವ ಅಧಿಸೂಚನೆಗಳ ಕಡೆಗೆ ಮಾತ್ರ ಗಮನಹರಿಸಲು ಸಹಾಯ ಮಾಡುತ್ತದೆ. ಯಾವಾಗಲೂ ನಿಶ್ಯಬ್ದ."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"ಪ್ರಾಶಸ್ತ್ಯದ ಅಧಿಸೂಚನೆಗಳ ಕೆಳಗೆ ಪ್ರದರ್ಶಿತವಾಗುತ್ತದೆ. ಯಾವಾಗಲೂ ನಿಶ್ಯಬ್ದ."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"ಪ್ರಾಶಸ್ತ್ಯದ ಅಧಿಸೂಚನೆಗಳ ಕೆಳಗೆ ಪ್ರದರ್ಶಿತವಾಗುತ್ತದೆ. ಯಾವಾಗಲೂ ನಿಶ್ಯಬ್ದ."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"ಪ್ರಾಶಸ್ತ್ಯದ ಅಧಿಸೂಚನೆಗಳ ಕೆಳಗೆ ಪ್ರದರ್ಶಿತವಾಗುತ್ತದೆ. ಯಾವಾಗಲೂ ನಿಶ್ಯಬ್ದ."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"ಧ್ವನಿ ಮತ್ತು ಸ್ಥಿತಿ ಪಟ್ಟಿ ಐಕಾನ್ ಮೂಲಕ ನಿಮ್ಮ ಗಮನವನ್ನು ಸೆಳೆಯುತ್ತದೆ. ಲಾಕ್ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ತೋರಿಸುತ್ತದೆ."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"ನಿಶ್ಶಬ್ದ"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"ಎಚ್ಚರಿಸಲಾಗುತ್ತಿದೆ"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"ಶಬ್ದ ಅಥವಾ ವೈಬ್ರೇಷನ್ ಇರದಂತೆ ನಿಮಗೆ ಗಮನಹರಿಸಲು ಸಹಾಯ ಮಾಡುತ್ತದೆ."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"ಧ್ವನಿ ಅಥವಾ ವೈಬ್ರೇಷನ್ ಮೂಲಕ ನಿಮ್ಮ ಗಮನವನ್ನು ಸೆಳೆಯುತ್ತದೆ."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"ಈ ಅಧಿಸೂಚನೆಗಳನ್ನು ಮಾರ್ಪಡಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"ಈ ಗುಂಪಿನ ಅಧಿಸೂಚನೆಗಳನ್ನು ಇಲ್ಲಿ ಕಾನ್ಫಿಗರ್ ಮಾಡಲಾಗಿರುವುದಿಲ್ಲ"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"ಪ್ರಾಕ್ಸಿ ಮಾಡಿದ ಅಧಿಸೂಚನೆಗಳು"</string>
@@ -935,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"ಸ್ಕ್ರೀನ್ನ ಎಡ ಕೆಳಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"ಕೆಳಗಿನ ಬಲಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"ವಜಾಗೊಳಿಸಿ"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index e40ab6f..af6f6eb 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB 디버깅이 허용되지 않음"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"현재 이 기기에 로그인한 사용자는 USB 디버깅을 사용 설정할 수 없습니다. 이 기능을 사용하려면 기본 사용자로 전환하세요."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB 포트 비활성화됨"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"기기를 액체나 이물질로부터 보호하기 위해 USB 포트가 사용 중지되었으며 액세서리를 연결할 수 없습니다.\n\nUSB 포트를 다시 안전하게 사용할 수 있게 되면 알려 드리겠습니다."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"충전기와 액세서리를 감지할 수 있도록 USB 포트가 사용 설정됨"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB 사용"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"자세히 알아보기"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"전체화면 모드로 확대"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"전체화면 모드로 확대"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"스크린샷"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"다시 표시 안함"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"모두 지우기"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"관리"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"무음 알림"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"무음 알림 모두 삭제"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"방해 금지 모드로 일시중지된 알림"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"시작하기"</string>
<string name="empty_shade_text" msgid="708135716272867002">"알림 없음"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"차단"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"계속 표시하기"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"최소화"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"무음"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"계속 무음으로 알림"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"알림"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"계속 알림"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"알림 사용 중지"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"이 앱의 알림을 계속 표시하시겠습니까?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"조용히 표시됨"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"우선순위 지정됨"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"풀다운 창에만 알림을 표시하여 집중에 방해가 되지 않습니다. 항상 음소거됩니다."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"우선순위 알림 아래에 표시됩니다. 항상 음소거됩니다."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"우선순위 알림 아래에 표시됩니다. 항상 음소거됩니다."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"우선순위 알림 아래에 표시됩니다. 항상 음소거됩니다."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"소리 및 상태 표시줄 아이콘으로 주의를 끕니다. 잠금 화면에 표시됩니다."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"무음"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"주의를 끄는 알림"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"소리나 진동 없이 집중할 수 있도록 도와줍니다"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"소리나 진동으로 주의를 끕니다"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"이 알림은 수정할 수 없습니다."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"이 알림 그룹은 여기에서 설정할 수 없습니다."</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"프록시를 통한 알림"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"허용"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"거부"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"탭하여 절전 모드 예약"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"배터리가 소진될 것 같으면 사용 설정"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"사용 안함"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"절전 모드 예약 사용 설정됨"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"배터리가 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 아래로 내려가면 절전 모드가 자동으로 켜집니다."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"왼쪽 하단으로 이동"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"오른쪽 하단으로 이동"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"닫기"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index dbeea5b..7f5a011 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB мүчүлүштүктөрүн оңдоого уруксат жок"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Учурда бул аккаунтта USB аркылуу мүчүлүштүктөрдү оңдоо функциясын иштетүүгө болбойт. Негизги колдонуучунун аккаунтуна кириңиз."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB порту өчүрүлдү"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Түзмөгүңүздүн ичине суюктук же булганч нерселер кирип кетпеши үчүн USB порту өчүрүлдү. Азырынча ал аркылуу башка түзмөктөргө туташууга болбойт.\n\nUSB портун кайра колдонуу мүмкүн болгондо, билдирме аласыз."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Кубаттагычтарды жана аксессуарларды аныктоо үчүн USB оюкчасы иштетилди"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB’ни иштетүү"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Кененирээк"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Экрнд тлтр ү. чен өлч өзг"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Экранды толтуруу ү-н чоюу"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Скриншот"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Экинчи көрсөтүлбөсүн"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Бардыгын тазалап салуу"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Башкаруу"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Үнсүз билдирмелер"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Маанилүү эмес билдирмелердин баарын өчүрүү"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"\"Тынчымды алба\" режиминде билдирмелер тындырылды"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Азыр баштоо"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Билдирме жок"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Бөгөттөө"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Көрсөтүлө берсин"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Кичирейтүү"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Үнсүз"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Үнү чыкпасын"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Билдирүү"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Кабар бериле берсин"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Билдирмелерди өчүрүү"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Бул колдонмонун эскертмелери көрсөтүлө берсинби?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Маанилүү эмес"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Маанилүү"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Тигинен жайгашкан экрандагы билдирмелерге гана фокустоого жардам берет. Ар дайым үнсүз."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Маанилүү билдирмелердин ылдый жагында чагылдырылат. Ар дайым үнсүз."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Маанилүү билдирмелердин ылдый жагында чагылдырылат. Ар дайым үнсүз."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Маанилүү билдирмелердин ылдый жагында чагылдырылат. Ар дайым үнсүз."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Билдирме келгенде атайын үн чыгат же абал тилкесинде сүрөтчө пайда болот. Билдирмелер кулпуланган экранда көрүнөт."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Үнсүз"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Шашылыш билдирме"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Үн же дирилдөөсүз ой топтоого жардам берет."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Үн чыгарып же дирилдеп көңүлүңүздү бурат."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Бул билдирмелерди өзгөртүүгө болбойт."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Бул билдирмелердин тобун бул жерде конфигурациялоого болбойт"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Прокси билдирмеси"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Уруксат берүү"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Жок"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Батареяны үнөмдөгүчтүн тартибин жөндөө үчүн басыңыз"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Батареянын кубаты түгөнүп калганда, күйгүзүлсүн"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Жок, рахмат"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Батареяны үнөмдөгүчтүн тартиби күйгүзүлдү"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Батареянын деңгээли <xliff:g id="PERCENTAGE">%d</xliff:g>%% төмөндөгөндө, Батареяны үнөмдөгүч режими автоматтык түрдө күйөт."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Төмөнкү сол жакка жылдыруу"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Төмөнкү оң жакка жылдырыңыз"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Жабуу"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index fa1c32a..4a1589d 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"ບໍ່ອະນຸຍາດໃຫ້ມີການແກ້ໄຂບັນຫາ USB"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ຜູ້ໃຊ້ທີ່ກຳລັງເຂົ້າສູ່ລະບົບອຸປະກອນຢູ່ໃນຕອນນີ້ບໍ່ສາມາດເປີດໃຊ້ການດີບັກ USB ໄດ້. ເພື່ອໃຊ້ຄຸນສົມບັດນີ້, ໃຫ້ສະຫຼັບໄປໃຊ້ຜູ້ໃຊ້ຫຼັກ."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"ປິດການນຳໃຊ້ຜອດ USB ແລ້ວ"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"ເພື່ອປົກປ້ອງອຸປະກອນຂອງທ່ານຈາກຂອງແຫລວ ຫຼື ເສດດິນຕ່າງໆ, ຜອດ USB ຈຶ່ງຖືກປິດການນຳໃຊ້ ແລະ ຈະບໍ່ກວດຫາອຸປະກອນເສີມໃດໆ.\n\nທ່ານຈະໄດ້ຮັບການແຈ້ງເຕືອນເມື່ອສາມາດໃຊ້ຜອດ USB ໄດ້ອີກເທື່ອໜຶ່ງ."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"ເປີດນຳໃຊ້ USB ແລ້ວເພື່ອກວດຫາສາຍສາກ ແລະ ອຸປະກອນເສີມ"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"ເປີດໃຊ້ USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"ສຶກສາເພີ່ມເຕີມ"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"ຊູມໃຫ້ເຕັມໜ້າຈໍ"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"ປັບໃຫ້ເຕັມໜ້າຈໍ"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"ພາບໜ້າຈໍ"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"ບໍ່ຕ້ອງສະແດງອີກ"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"ລຶບລ້າງທັງໝົດ"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"ຈັດການ"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"ການແຈ້ງເຕືອນແບບງຽບ"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"ລຶບລ້າງການແຈ້ງເຕືອນແບບງຽບທັງໝົດ"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"ຢຸດການແຈ້ງເຕືອນໂດຍໂໝດຫ້າມລົບກວນແລ້ວ"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"ເລີ່ມດຽວນີ້"</string>
<string name="empty_shade_text" msgid="708135716272867002">"ບໍ່ມີການແຈ້ງເຕືອນ"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"ບລັອກ"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"ສະແດງຕໍ່ໄປ"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"ຫຍໍ້"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"ປິດສຽງ"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"ສືບຕໍ່ມິດງຽບ"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"ການເຕືອນ"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"ສືບຕໍ່ແຈ້ງເຕືອນ"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"ປິດການແຈ້ງເຕືອນ"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"ສະແດງການແຈ້ງເຕືອນຈາກແອັບນີ້ຕໍ່ໄປບໍ?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"ສຸພາບ"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"ສຳຄັນ"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"ຊ່ວຍທ່ານມີສະມາທິກກັບການແຈ້ງເຕືອນສະເພາະໃນແຖບເລື່ອນລົງເທົ່ານັ້ນ. ປິດສຽງຕະຫຼອດ."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"ສະແດງການແຈ້ງເຕືອນທີ່ຄວາມສຳຄັນຕ່ຳລົງ. ປິດສຽງຕະຫຼອດ."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"ສະແດງການແຈ້ງເຕືອນທີ່ຄວາມສຳຄັນຕ່ຳລົງ. ປິດສຽງຕະຫຼອດ."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"ສະແດງການແຈ້ງເຕືອນທີ່ຄວາມສຳຄັນຕ່ຳລົງ. ປິດສຽງຕະຫຼອດ."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"ດຶງຄວາມສົນໃຈຂອງທ່ານດ້ວຍສຽງ ແລະ ໄອຄອນແຖບສະຖານະ. ສະແດງຢູ່ໜ້າຈໍລັອກ."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"ປິດສຽງ"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"ການເຕືອນ"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"ຊ່ວຍທ່ານມີສະມາທິໂດຍບໍ່ໃຊ້ສຽງ ຫຼື ການສັ່ນເຕືອນ."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"ດຶງຄວາມສົນໃຈຂອງທ່ານດ້ວຍສຽງ ຫຼື ການສັ່ນເຕືອນ."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"ບໍ່ສາມາດແກ້ໄຂການແຈ້ງເຕືອນເຫຼົ່ານີ້ໄດ້."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"ບໍ່ສາມາດຕັ້ງຄ່າກຸ່ມການແຈ້ງເຕືອນນີ້ຢູ່ບ່ອນນີ້ໄດ້"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"ການແຈ້ງເຕືອນແບບພຣັອກຊີ"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"ອະນຸຍາດ"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"ປະຕິເສດ"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"ແຕະເພື່ອຕັ້ງການເປີດຕົວປະຢັດແບັດເຕີຣີ"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"ເປີດໃຊ້ເມື່ອແບັດເຕີຣີໜ້າຈະໃກ້ໝົດ"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"ບໍ່, ຂອບໃຈ"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"ຕັ້ງໃຫ້ເປີດຕົວປະຢັດແບັດເຕີຣີແລ້ວ"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"ຕົວປະຢັດແບັດເຕີຣີຈະເປີດຂຶ້ນມາໂດຍອັດຕະໂນມັດເມື່ອແບັດເຕີຣີຕ່ຳກວ່າ <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"ຍ້າຍຊ້າຍລຸ່ມ"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"ຍ້າຍຂວາລຸ່ມ"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"ປິດໄວ້"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 88846d9..ab6a044 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB derinimas neleidžiamas"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Šiuo metu prie įrenginio prisijungęs naudotojas negali įjungti USB derinimo. Kad galėtumėte naudoti šią funkciją, perjunkite į pagrindinį naudotoją."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB prievadas išjungtas"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Siekiant apsaugoti įrenginį nuo skysčių ar smulkių dalelių, USB prievadas buvo išjungtas ir neaptiks jokių priedų.\n\nJums bus pranešta, kai galėsite vėl saugiai naudoti USB prievadą."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB prievadas įgalintas aptikti kroviklius ir priedus"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Įgalinti USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Sužinokite daugiau"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Keisti mast., kad atit. ekr."</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Ištempti, kad atit. ekr."</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Ekrano kopija"</string>
@@ -464,10 +462,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Daugiau neberodyti"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Viską išvalyti"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Tvarkyti"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Tylūs pranešimai"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Išvalyti visus tylius pranešimus"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Pranešimai pristabdyti naudojant netrukdymo režimą"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Pradėti dabar"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Nėra įspėjimų"</string>
@@ -651,21 +647,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Blokuoti"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Toliau rodyti"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Sumažinti"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Tylūs"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Neskambėti"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Įspėti"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Toliau įspėti"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Išjungti pranešimus"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Toliau rodyti iš šios programos gautus pranešimus?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Netrikdantys"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Pirmenybiniai"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Padeda susikaupti, nes pranešimai pateikiami tik išskleidžiamajame skydelyje. Visada nutildyti."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Pateikiami po prioritetiniais pranešimais. Visada nutildyti."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Pateikiami po prioritetiniais pranešimais. Visada nutildyti."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Pateikiami po prioritetiniais pranešimais. Visada nutildyti."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Dėmesį atkreipia garsas ir būsenos juostos piktograma. Rodomi užrakintame ekrane."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Tylūs"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Įspėti"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Padeda atkreipti dėmesį be garso arba vibravimo."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Atkreipia dėmesį garsu arba vibravimu."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Šių pranešimų keisti negalima."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Šios grupės pranešimai čia nekonfigūruojami"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Per tarpinį serverį gautas pranešimas"</string>
@@ -918,8 +909,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Leisti"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Neleisti"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Palietę planuokite akumuliatoriaus tausojimo priemonės veikimą"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Įjunkite, jei akumuliatorius gali greitai išsekti"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Ne, ačiū"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Akumuliatoriaus tausojimo priemonės veikimas suplanuotas"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Akumuliatoriaus tausojimo priemonė bus įjungta automatiškai akumuliatoriaus įkrovai pasiekus mažiau nei <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -952,4 +942,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Perkelti į apačią kairėje"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Perkelti į apačią dešinėje"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Atmesti"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 5719dc1..889629e 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB atkļūdošana nav atļauta"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Lietotājs, kurš pašlaik ir pierakstījies šajā ierīcē, nevar iespējot USB atkļūdošanu. Lai izmantotu šo funkciju, pārslēdzieties uz galveno lietotāju."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB pieslēgvieta atspējota"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Lai aizsargātu ierīci no šķidruma un gružiem, USB pieslēgvieta ir atspējota un tajā nevarēs noteikt pieslēgtus piederumus.\n\nKad USB pieslēgvietu atkal drīkstēs izmantot, saņemsiet paziņojumu."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB portam ir iespējota uzlādes ierīču un piederumu noteikšana"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Iespējot USB portu"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Uzzināt vairāk"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Tālumm., lai aizp. ekr."</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Stiepiet, lai aizp. ekr."</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Ekrānuzņēmums"</string>
@@ -461,10 +459,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Vairs nerādīt"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Dzēst visu"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Pārvaldīt"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Klusie paziņojumi"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Notīrīt visus klusos paziņojumus"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Paziņojumi pārtraukti, izmantojot iestatījumu “Netraucēt”"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Sākt tūlīt"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Nav paziņojumu"</string>
@@ -648,21 +644,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Bloķēt"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Turpināt rādīt"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizēt"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Klusums"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Neslēgt skaļumu"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Saņemt brīdinājumus"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Turpināt paziņošanu"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Izslēgt paziņojumus"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Vai turpināt rādīt paziņojumus no šīs lietotnes?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Neuzkrītoši"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioritāri"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Lai netraucētu jums koncentrēties, paziņojumi tiek rādīti tikai nolaižamajā panelī. Vienmēr bez skaņas."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Tiek rādīts zem prioritārajiem paziņojumiem. Vienmēr bez skaņas."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Tiek rādīts zem prioritārajiem paziņojumiem. Vienmēr bez skaņas."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Tiek rādīts zem prioritārajiem paziņojumiem. Vienmēr bez skaņas."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Lai piesaistītu jūsu uzmanību, tiek atskaņots signāls un tiek rādīta statusa joslas ikona. Paziņojums ir redzams bloķēšanas ekrānā."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Klusums"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Brīdinājumu saņemšana"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Palīdz jums koncentrēties, nenovēršot uzmanību ar skaņu vai vibrāciju."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Jūsu uzmanība tiek piesaistīta ar skaņas vai vibrācijas signālu."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Šos paziņojumus nevar modificēt."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Šeit nevar konfigurēt šo paziņojumu grupu."</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Starpniekservera paziņojums"</string>
@@ -913,8 +904,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Atļaut"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Neatļaut"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Pieskarieties, lai iestatītu akumulatora jaudas taupīšanas režīma grafiku"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Ieslēgt, ja akumulators var izlādēties"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Nē, paldies"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Ieslēgts akumulatora enerģijas taupīšanas režīma grafiks"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Tiklīdz akumulatora uzlādes līmenis būs zemāks nekā <xliff:g id="PERCENTAGE">%d</xliff:g>%%, tiks automātiski ieslēgts akumulatora jaudas taupīšanas režīms."</string>
@@ -947,4 +937,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Pārvietot apakšpusē pa kreisi"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Pārvietot apakšpusē pa labi"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Nerādīt"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 86109b8..d6f2ae47 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Отстранувањето грешки на USB не е дозволено"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Корисникот што моментално е најавен на уредов не може да вклучи отстранување грешки на USB. За да ја користите функцијава, префрлете се на примарниот корисник."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB-портата е оневозможена"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"За да го заштитиме уредот од течност или нечистотија, USB-портата е оневозможена и нема да ги открива додатоците.\n\nЌе ве известиме кога ќе биде во ред да ја користите USB-портата повторно."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB-портата е овозможена за откривање полначи и додатоци"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Овозможи USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Дознајте повеќе"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Зумирај да се исполни екранот"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Растегни да се исполни екранот"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Слика од екранот"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Не покажувај повторно"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Исчисти сè"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Управувајте"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Тивки известувања"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Исчисти ги сите тивки известувања"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Известувањата се паузирани од „Не вознемирувај“"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Започни сега"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Нема известувања"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Блокирај"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Продолжи да ги прикажуваш"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Минимизирај"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Тивко"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Продолжи со безгласно прикажување"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Предупредувај"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Продолжи да ме предупредуваш"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Исклучи известувања"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Дали да продолжат да се прикажуваат известувања од апликацијава?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Тивко"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Приоритетно"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Не ви го одвлекува вниманието прикажувајќи известувања само во списокот со известувања. Секогаш безгласно."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Се прикажува под приоритетните известувања. Секогаш безгласно."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Се прикажува под приоритетните известувања. Секогаш безгласно."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Се прикажува под приоритетните известувања. Секогаш безгласно."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Ви го привлекува вниманието со звук и икона во статусната лента. Се прикажува на заклучен екран."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Тивко"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Предупредувај"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Ви помага да се концентрирате без звук или вибрации."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Ви го привлекува вниманието со звук или вибрации."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Овие известувања не може да се изменат"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Оваа група известувања не може да се конфигурира тука"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Известување преку прокси"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Дозволи"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Одбиј"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Допрете за да закажете „Штедач на батерија“"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Вклучи ако е веројатно дека батеријата ќе се испразни"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Не, фала"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Распоредот за „Штедач на батерија“ е вклучен"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Штедачот на батерија ќе се вклучи автоматски кога батеријата ќе падне под <xliff:g id="PERCENTAGE">%d</xliff:g> %%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Премести долу лево"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Премести долу десно"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Отфрли"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index cd7cb67..5f4e499 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB ഡീബഗ്ഗിംഗ് അനുവദനീയമല്ല"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ഉപകരണത്തിൽ ഇപ്പോൾ സൈൻ ഇൻ ചെയ്തിരിക്കുന്ന ഉപയോക്താവിന് USB ഡീബഗ്ഗിംഗ് ഓണാക്കാനാകില്ല. ഈ ഫീച്ചർ ഉപയോഗിക്കാൻ പ്രാഥമിക ഉപയോക്താവിലേക്ക് മാറുക."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB പോർട്ട് പ്രവർത്തനരഹിതമാക്കി"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"ദ്രാവകത്തിൽ നിന്നോ പൊടിയിൽ നിന്നോ നിങ്ങളുടെ ഉപകരണത്തെ പരിരക്ഷിക്കാനായി USB പോർട്ട് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നതിനാൽ അത് ആക്സസറികളൊന്നും തിരിച്ചറിയില്ല.\n\n USB പോർട്ട് വീണ്ടും ഉപയോഗിക്കാനാകുമ്പോൾ നിങ്ങളെ അറിയിക്കും."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"ആക്സസറികളും ചാർജറുകളും കണ്ടെത്താൻ USB പോർട്ട് പ്രവർത്തനക്ഷമമാക്കുക"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB പ്രവർത്തനക്ഷമമാക്കുക"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"കൂടുതലറിയുക"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"സ്ക്രീനിൽ ഉൾക്കൊള്ളിക്കാൻ സൂം ചെയ്യുക"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"സ്ക്രീനിൽ ഉൾക്കൊള്ളിക്കാൻ വലിച്ചുനീട്ടുക"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"സ്ക്രീൻഷോട്ട്"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"വീണ്ടും കാണിക്കരുത്"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"എല്ലാം മായ്ക്കുക"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"മാനേജ് ചെയ്യുക"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"നിശബ്ദ അറിയിപ്പുകൾ"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"എല്ലാ നിശബ്ദ അറിയിപ്പുകളും മായ്ക്കുക"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"\'ശല്യപ്പെടുത്തരുത്\' വഴി അറിയിപ്പുകൾ താൽക്കാലികമായി നിർത്തി"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"ഇപ്പോൾ ആരംഭിക്കുക"</string>
<string name="empty_shade_text" msgid="708135716272867002">"അറിയിപ്പുകൾ ഒന്നുമില്ല"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"ബ്ലോക്ക് ചെയ്യുക"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"തുടർന്നും കാണിക്കുക"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"ചെറുതാക്കുക"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"നിശബ്ദം"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"നിശബ്ദമായ നിലയിൽ തുടരുക"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"മുന്നറിയിപ്പ് നൽകൽ"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"മുന്നറിയിപ്പ് നൽകുന്നത് തുടരുക"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"അറിയിപ്പുകൾ ഓഫാക്കുക"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"ഈ ആപ്പിൽ നിന്നുള്ള അറിയിപ്പുകൾ തുടർന്നും കാണിക്കണോ?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"സൗമ്യമായ"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"മുൻഗണനയുള്ള"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"പുൾ ഡൗൺ ഷെയ്ഡിൽ മാത്രമുള്ള അറിയിപ്പുകളിൽ ശ്രദ്ധ കേന്ദ്രീകരിക്കാൻ നിങ്ങളെ സഹായിക്കുന്നു. എപ്പോഴും നിശബ്ദം."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"കുറഞ്ഞ പ്രാധാന്യമുള്ള മുൻഗണനാ അറിയിപ്പുകൾ പ്രദർശിപ്പിക്കുന്നു. എപ്പോഴും നിശബ്ദം."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"കുറഞ്ഞ പ്രാധാന്യമുള്ള മുൻഗണനാ അറിയിപ്പുകൾ പ്രദർശിപ്പിക്കുന്നു. എപ്പോഴും നിശബ്ദം."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"കുറഞ്ഞ പ്രാധാന്യമുള്ള മുൻഗണനാ അറിയിപ്പുകൾ പ്രദർശിപ്പിക്കുന്നു. എപ്പോഴും നിശബ്ദം."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"ശബ്ദവും സ്റ്റാറ്റസ് ബാർ ഐക്കണും ഉപയോഗിച്ച് ശ്രദ്ധ ക്ഷണിക്കുന്നു. ലോക്ക് സ്ക്രീനിൽ കാണിക്കും."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"നിശബ്ദം"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"മുന്നറിയിപ്പ് നൽകൽ"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"ശബ്ദമോ വൈബ്രേഷനോ ഇല്ലാതെ ശ്രദ്ധ കേന്ദ്രീകരിക്കാൻ നിങ്ങളെ സഹായിക്കുന്നു."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"ശബ്ദമോ വെെബ്രേഷനോ ഉപയോഗിച്ച് നിങ്ങളുടെ ശ്രദ്ധ ക്ഷണിക്കുന്നു."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"ഈ അറിയിപ്പുകൾ പരിഷ്ക്കരിക്കാനാവില്ല."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"അറിയിപ്പുകളുടെ ഈ ഗ്രൂപ്പ് ഇവിടെ കോണ്ഫിഗര് ചെയ്യാൻ കഴിയില്ല"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"പ്രോക്സി അറിയിപ്പ്"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"അനുവദിക്കുക"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"നിരസിക്കുക"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"ബാറ്ററി ലാഭിക്കൽ ഷെഡ്യൂൾ ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"ബാറ്ററി ചാർജ് തീരാൻ സാധ്യതയുണ്ടെങ്കിൽ ഓണാക്കുക"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"വേണ്ട"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"ബാറ്ററി ലാഭിക്കൽ ഷെഡ്യൂൾ ഓണാക്കുക"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"ബാറ്ററി <xliff:g id="PERCENTAGE">%d</xliff:g>%% ൽ താഴെയാകുമ്പോൾ, ബാറ്ററി ലാഭിക്കൽ സ്വമേധയാ ഓണാകും."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"ചുവടെ ഇടതുഭാഗത്തേക്ക് നീക്കുക"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"ചുവടെ വലതുഭാഗത്തേക്ക് നീക്കുക"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"ഡിസ്മിസ് ചെയ്യുക"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 5630e0d..4ed8fcc 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB алдаа засалт хийх боломжгүй"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Энэ төхөөрөмжид нэвтэрсэн хэрэглэгч USB дебаг хийх онцлогийг асаах боломжгүй байна. Энэ онцлогийг ашиглахын тулд үндсэн хэрэглэгч рүү сэлгэнэ үү."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB портыг идэвхгүй болгосон"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Таны төхөөрөмжийг шингэн зүйл эсвэл бохирдлоос хамгаалахын тулд USB портыг идэвхгүй болгосон бөгөөд энэ нь ямар ч дагалдах хэрэгслийг илрүүлэхгүй.\n\nТанд USB портыг дахин ашиглахад аюулгүй болох үед мэдэгдэх болно."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Цэнэглэгч болон нэмэлт хэрэгслийг илрүүлэхийн тулд USB портыг идэвхжүүлсэн"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB-г идэвхжүүлэх"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Нэмэлт мэдээлэл авах"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Дэлгэц дүүргэх бол өсгөнө үү"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Дэлгэц дүүргэх бол татна уу"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Дэлгэцийн зураг дарах"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Дахиж үл харуулах"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Бүгдийг арилгах"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Удирдах"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Чимээгүй мэдэгдэл"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Бүх чимээгүй мэдэгдлийг арилгах"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Бүү саад бол горимын түр зогсоосон мэдэгдэл"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Одоо эхлүүлэх"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Мэдэгдэл байхгүй"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Блоклох"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Харуулсан хэвээр байх"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Багасгах"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Чимээгүй"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Чимээгүй хэвээр харуулах"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Сэрэмжлүүлж байна"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Үргэлжлүүлэн сануулах"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Мэдэгдлийг унтраах"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Энэ аппаас мэдэгдэл харуулсан хэвээр байх уу?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Бага ач холбогдолтой"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Чухал ач холбогдолтой"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Танд зөвхөн доош татдаг сүүдрийн мэдэгдлээр төвлөрөхөд тусалдаг. Үргэлж чимээгүй байна."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Доорх ач холбогдолтой мэдэгдлийг харуулдаг. Үргэлж чимээгүй байна."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Доорх ач холбогдолтой мэдэгдлийг харуулдаг. Үргэлж чимээгүй байна."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Доорх ач холбогдолтой мэдэгдлийг харуулдаг. Үргэлж чимээгүй байна."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Таны анхаарлыг дуу болон статус самбарын дүрс тэмдгээр татдаг. Түгжигдсэн дэлгэц дээр харуулдаг."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Чимээгүй"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Дуутай"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Дуу эсвэл чичиргээгүйгээр танд төвлөрөхөд тусална."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Дуу эсвэл чичиргээгүйгээр таны анхаарлыг татна."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Эдгээр мэдэгдлийг өөрчлөх боломжгүй."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Энэ бүлэг мэдэгдлийг энд тохируулах боломжгүй байна"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Прокси хийсэн мэдэгдэл"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Зөвшөөрөх"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Татгалзах"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Тэжээл хэмнэгч онцлогийг хуваарилахын тулд товших"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Батарей дуусах гэж байгаа үед асаана уу"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Үгүй, баярлалаа"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Тэжээл хэмнэгч онцлогийн хуваарийг асаасан"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Батарей <xliff:g id="PERCENTAGE">%d</xliff:g>%%-с бага болсон үед Тэжээл хэмнэгч автоматаар асна."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Зүүн доош зөөх"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Баруун доош зөөх"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Үл хэрэгсэх"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 34c31e1..65034a3 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB डीबग करण्यास अनुमती नाही"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"सध्या या डीव्हाइसमध्ये साइन इन केलेला वापरकर्ता USB डीबग करणे चालू करू शकत नाही. हे वैशिष्ट्य वापरण्यासाठी, प्राथमिक वापरकर्त्यावर स्विच करा."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB पोर्ट बंद करा"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"तुमच्या डिव्हाइसला ओलावा किंवा धूळीपासून संरक्षित करण्यासाठी, USB पोर्ट बंद आहे आणि अॅक्सेसरी डिटेक्ट करणार नाही. \n\n तुम्हाला USB पोर्ट पुन्हा वापरणे ठीक आहे तेव्हा सूचित केले जाईल."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"चार्जर आणि अॅक्सेसरी शोधण्यासाठी USB पोर्ट सुरू केलेले आहे"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB सुरू करा"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"अधिक जाणून घ्या"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"स्क्रीन भरण्यासाठी झूम करा"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"स्क्रीन भरण्यासाठी ताणा"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"स्क्रीनशॉट"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"पुन्हा दर्शवू नका"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"सर्व साफ करा"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"व्यवस्थापित करा"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"सायलंट सूचना"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"सर्व सायलंट सूचना साफ करा"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"व्यत्यय आणून नकाद्वारे सूचना थांबवल्या"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"आता सुरू करा"</string>
<string name="empty_shade_text" msgid="708135716272867002">"सूचना नाहीत"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"ब्लॉक करा"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"दाखवणे सुरू ठेवा"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"लहान करा"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"सायलंट"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"सायलंट रहा"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"सूचना देत आहे"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"सूचना देत रहा"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"सूचना बंद करा"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"या अॅपकडील सूचना दाखवणे सुरू ठेवायचे?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"हळू आवाजातील"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"प्राधान्यकृत"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"फक्त पुल डाउन शेडमध्ये सूचनांवर लक्ष केंद्रीत करण्यात मदत करते. नेहमी सायलंट."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"कमी प्राधान्य असलेल्या सूचना दाखवते. नेहमी सायलंट."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"कमी प्राधान्य असलेल्या सूचना दाखवते. नेहमी सायलंट."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"कमी प्राधान्य असलेल्या सूचना दाखवते. नेहमी सायलंट."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"आवाज आणि स्टेटस बार आयकनसह तुमचे लक्ष वेधून घेते. लॉक स्क्रीनवर दाखवते."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"सायलंट"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"सूचना देत आहे"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"आवाज किंवा व्हायब्रेशनशिवाय तुम्हाला लक्ष केंद्रित करण्यास मदत करते."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"आवाज किंवा व्हायब्रेशनने तुमचे लक्ष वेधून घेते."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"या सूचनांमध्ये सुधारणा केली जाऊ शकत नाही."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"या सूचनांचा संच येथे कॉन्फिगर केला जाऊ शकत नाही"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"प्रॉक्सी केलेल्या सूचना"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"अनुमती द्या"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"नकार द्या"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"बॅटरी बचतकर्ता शेड्यूल करण्यासाठी टॅप करा"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"बॅटरी संपण्याची शक्यता असल्यास सुरू करा"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"नाही नको"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"बॅटरी बचतकर्ता शेड्यूल सुरू केले आहे"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"बॅटरी <xliff:g id="PERCENTAGE">%d</xliff:g>%% पेक्षा खाली गेल्यास बॅटरी सेव्हर आपोआप सुरू होईल."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"तळाशी डावीकडे हलवा"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"तळाशी उजवीकडे हलवा"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"डिसमिस करा"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 716c24f..9669077 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Penyahpepijatan USB tidak dibenarkan"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Pengguna yang log masuk ke peranti ini pada masa ini tidak boleh menghidupkan penyahpepijatan USB. Untuk menggunakan ciri ini, tukar kepada pengguna utama."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Port USB dilumpuhkan"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Untuk melindungi peranti anda daripada cecair atau serpihan, port USB dilumpuhkan dan tidak akan mengesan sebarang aksesori.\n\nAnda akan dimaklumi apabila selamat untuk menggunakan port USB lagi."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Port USB didayakan untuk mengesan pengecas dan aksesori"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Dayakan USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Ketahui lebih lanjut"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zum untuk memenuhi skrin"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Regang utk memenuhi skrin"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Tangkapan skrin"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Jangan tunjukkan lagi"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Kosongkan semua"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Urus"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Pemberitahuan senyap"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Kosongkan semua pemberitahuan senyap"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Pemberitahuan dijeda oleh Jangan Ganggu"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Mulakan sekarang"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Tiada pemberitahuan"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Sekat"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Terus tunjukkan"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimumkan"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Senyap"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Kekal senyap"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Memaklumi"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Teruskan memberikan makluman"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Matikan pemberitahuan"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Terus tunjukkan pemberitahuan daripada apl ini?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Lembut"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Diutamakan"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Membantu anda fokus dengan memaparkan pemberitahuan sahaja dalam bidai tarik turun. Sentiasa senyap."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Dipaparkan di bawah pemberitahuan keutamaan. Sentiasa senyap."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Dipaparkan di bawah pemberitahuan keutamaan. Sentiasa senyap."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Dipaparkan di bawah pemberitahuan keutamaan. Sentiasa senyap."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Menarik perhatian anda dengan bunyi & ikon bar status. Ditunjukkan pada skrin kunci."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Senyap"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Memaklumi"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Membantu anda fokus tanpa bunyi atau getaran."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Menarik perhatian anda dengan bunyi atau getaran."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Pemberitahuan ini tidak boleh diubah suai."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Kumpulan pemberitahuan ini tidak boleh dikonfigurasikan di sini"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Pemberitahuan berproksi"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Benarkan"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Tolak"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Ketik untuk menjadualkan Penjimat Bateri"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Hidupkan apabila bateri berkemungkinan habis"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Tidak perlu"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Jadual Penjimat Bateri dihidupkan"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Penjimat Bateri akan dihidupkan secara automatik setelah kuasa bateri kurang daripada <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Alihkan ke bawah sebelah kiri"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Alihkan ke bawah sebelah kanan"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Ketepikan"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index ef095b3..3734536 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -586,7 +586,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"အားမသွင်းနေစဉ်တွင် ဘတ်ထရီအဆင့် ရာခိုင်နှုန်းကို အခြေနေပြဘား အိုင်ကွန်တွင် ပြပါ"</string>
<string name="quick_settings" msgid="10042998191725428">"အမြန် ဆက်တင်များ"</string>
<string name="status_bar" msgid="4877645476959324760">"အခြေအနေပြနေရာ"</string>
- <string name="overview" msgid="4018602013895926956">"ခြုံငုံသုံးသပ်ချက်"</string>
+ <string name="overview" msgid="4018602013895926956">"အကျဉ်း"</string>
<string name="demo_mode" msgid="2532177350215638026">"စနစ် UI စရုပ်ပြမုဒ်"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"သရုပ်ပြမုဒ်ကို ဖွင့်ရန်"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"သရုပ်ပြမုဒ် ပြရန်"</string>
@@ -647,13 +647,10 @@
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"ဆက်လက် သတိပေးရန်"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"အကြောင်းကြားချက်များ ပိတ်ရန်"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"ဤအက်ပ်ထံမှ အကြောင်းကြားချက်များကို ဆက်ပြလိုပါသလား။"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"မသိမသာ"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"ဦးစားပေး"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"အပေါ်မှဆွဲချသည့် နေရာတွင်သာ အကြောင်းကြားချက်များကို အာရုံစိုက်ရန် အထောက်ကူပြုပေးသည်။ အမြဲ အသံတိတ်ရန်။"</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"ဦးစားပေးအကြောင်းကြားချက်များ၏ အောက်တွင်ဖော်ပြသည်။ အမြဲ အသံတိတ်ရန်။"</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"ဦးစားပေးအကြောင်းကြားချက်များ၏ အောက်တွင်ဖော်ပြသည်။ အမြဲ အသံတိတ်ရန်။"</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"ဦးစားပေးအကြောင်းကြားချက်များ၏ အောက်တွင်ဖော်ပြသည်။ အမြဲ အသံတိတ်ရန်။"</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"အသံ၊ အခြေအနေပြဘား သင်္ကေတတို့ဖြင့် သတိပေးသည်။ လော့ခ်ချထားချိန်မျက်နှာပြင်တွင် ပြသည်။"</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"အသံတိတ်ရန်"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"သတိပေးခြင်း"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"အသံ သို့မဟုတ် တုန်ခါမှု မပါဘဲ အာရုံစိုက်နိုင်စေရန် ကူညီပေးသည်။"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"အသံ သို့မဟုတ် တုန်ခါမှုဖြင့် အာရုံစိုက်လာအောင် ပြုလုပ်သည်။"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"ဤအကြောင်းကြားချက်များကို ပြုပြင်၍ မရပါ။"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"ဤအကြောင်းကြားချက်အုပ်စုကို ဤနေရာတွင် စီစဉ်သတ်မှတ်၍ မရပါ"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"ပရောက်စီထည့်ထားသော အကြောင်းကြားချက်"</string>
@@ -935,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"ဘယ်အောက်ခြေသို့ ရွှေ့ရန်"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"ညာအောက်ခြေသို့ ရွှေ့ပါ"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"ပယ်ရန်"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 70c0bb2..c10bc37 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB-feilsøking er ikke tillatt"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Brukeren som for øyeblikket er logget på denne enheten, kan ikke slå på USB-feilsøking. For å bruke denne funksjonen, bytt til hovedbrukeren."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB-porten er deaktivert"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"For å beskytte enheten din mot væsker eller rusk er USB-porten deaktivert og kan ikke oppdage tilbehør.\n\nDu blir varslet når det er trygt å bruke USB-porten igjen."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Registrering av ladere og tilbehør er slått på for USB-porten"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Slå på USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Finn ut mer"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom for å fylle skjermen"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Strekk for å fylle skjerm"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Skjermdump"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Ikke vis igjen"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Fjern alt"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Administrer"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Lydløse varsler"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Fjern alle lydløse varsler"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Varsler er satt på pause av «Ikke forstyrr»"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Start nå"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Ingen varsler"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Blokkér"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Fortsett å vise"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimer"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Lydløs"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Forbli lydløs"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Varsling"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Fortsett å varsle"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Slå av varsler"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Vil du fortsette å vise varsler fra denne appen?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Diskré"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioritert"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Hjelper deg med å fokusere, med varsler bare i nedtrekkspanelet. Alltid lydløs."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Vises under prioritetsvarsler. Alltid lydløs."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Vises under prioritetsvarsler. Alltid lydløs."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Vises under prioritetsvarsler. Alltid lydløs."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Får oppmerksomheten din med lyd og et ikon i statusfeltet. Vises på låseskjermen."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Lydløs"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Varsling"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Hjelper deg med å fokusere uten lyd eller vibrering."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Får oppmerksomheten din med lyd eller vibrering."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Disse varslene kan ikke endres."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Denne varselgruppen kan ikke konfigureres her"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Omdirigert varsel"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Tillat"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Avvis"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Trykk for å planlegge batterisparing"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Slå på når det er sannsynlig at du går tom for batteri"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Nei takk"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Tidsplan for batterisparing er slått på"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Batterisparing slås på automatisk når batteriet er lavere enn <xliff:g id="PERCENTAGE">%d</xliff:g> %%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Flytt til nederst til venstre"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Flytt til nederst til høyre"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Avvis"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 84989ee..239e207 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB डिबग गर्न अनुमति छैन"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"हाल यस यन्त्रमा साइन इन हुनुभएको प्रयोगकर्ताले USB डिबग सक्रिय गर्न सक्नुहुन्न। यो सुविधाको प्रयोग गर्न प्राथमिक प्रयोगकर्तामा बदल्नुहोस्।"</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB पोर्ट असक्षम पारियो"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"तपाईंको यन्त्रलाई तरल पदार्थ वा धुलोबाट जोगाउन यसको USB पोर्ट असक्षम पारिएको छ र यसले कुनै पनि सहायक उपकरणहरू पहिचान गर्ने छैन।\n\nउक्त USB पोर्ट फेरि प्रयोग गर्दा हुन्छ भने तपाईंलाई यसबारे सूचित गरिने छ।"</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"चार्जर तथा सामानहरू पत्ता लगाउन सक्षम पारिएको USB पोर्ट"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB सक्षम पार्नुहोस्"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"थप जान्नुहोस्"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"स्क्रिन भर्न जुम गर्नुहोस्"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"स्क्रिन भर्न तन्काउनुहोस्"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"स्क्रिनसट"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"फेरि नदेखाउनुहोस्"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"सबै हटाउनुहोस्"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"व्यवस्थित गर्नुहोस्"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"मौन सूचनाहरू"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"सबै मौन सूचनाहरू हटाउनुहोस्"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"बाधा नपुऱ्याउनुहोस् नामक मोडमार्फत पज पारिएका सूचनाहरू"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"अहिले सुरु गर्नुहोस्"</string>
<string name="empty_shade_text" msgid="708135716272867002">"कुनै सूचनाहरू छैनन्"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"रोक लगाउनुहोस्"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"देखाउने क्रम जारी राख्नुहोस्"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"सानो बनाउनुहोस्"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"मौन"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"मौन रहनुहोस्"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"सतर्क गराउने"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"सर्तक गराइरहनुहोस्"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"सूचनाहरू निष्क्रिय पार्नुहोस्"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"यो अनुप्रयोगका सूचनाहरू देखाउने क्रम जारी राख्ने हो?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"मौन"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"प्राथमिकता दिइएको"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"तपाईंलाई पुल डाउन सेडमा रहेका सूचनाहरूमा मात्र केन्द्रित हुन मद्दत गर्छ। सधैँ मौन।"</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"न्यून प्राथमिकताका सूचनाहरू देखाउँछ। सधैँ मौन।"</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"न्यून प्राथमिकताका सूचनाहरू देखाउँछ। सधैँ मौन।"</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"न्यून प्राथमिकताका सूचनाहरू देखाउँछ। सधैँ मौन।"</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"ध्वनि र वस्तुस्थिति पट्टीको आइकनमार्फत तपाईंको ध्यान खिच्छ। लक स्क्रिनमा देखाउँछ।"</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"मौन"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"सतर्क गराउँदै"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"तपाईंलाई आवाज वा कम्पनविना ध्यान केन्द्रित गर्न मद्दत गर्छ।"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"ध्वनि वा कम्पनमार्फत तपाईंको ध्यान आकर्षित गर्छ।"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"यी सूचनाहरू परिमार्जन गर्न मिल्दैन।"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"यहाँबाट सूचनाहरूको यो समूह कन्फिगर गर्न सकिँदैन"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"प्रोक्सीमार्फत आउने सूचना"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"अनुमति दिनुहोस्"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"अस्वीकार गर्नु…"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"ब्याट्री सेभरको समयतालिका बनाउन ट्याप गर्नुहोस्"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"ब्याट्री सकिने सम्भावना भएमा सक्रिय गर्नुहोस्"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"पर्दैन धन्यवाद"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"ब्याट्री सेभरको समयतालिका सक्रिय गरियो"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"ब्याट्री <xliff:g id="PERCENTAGE">%d</xliff:g>%% भन्दा कम भएको बेला ब्याट्री सेभर स्वतः सक्रिय हुने छ।"</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"पुछारमा बायाँतिर सार्नुहोस्"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"पुछारमा दायाँतिर सार्नुहोस्"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"हटाउनुहोस्"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index c5f4052..7b55d18 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -46,7 +46,7 @@
<color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color>
<color name="notification_guts_link_icon_tint">@color/GM2_grey_500</color>
- <color name="notification_guts_sub_text_color">@color/GM2_grey_500</color>
+ <color name="notification_guts_sub_text_color">@color/GM2_grey_300</color>
<color name="notification_guts_header_text_color">@color/GM2_grey_200</color>
<color name="notification_guts_info_button_color">@color/GM2_blue_300</color>
<color name="notification_guts_priority_button_content_color">@color/GM2_grey_500</color>
@@ -54,7 +54,7 @@
<color name="notification_guts_priority_button_bg_fill_color">@color/transparent</color>
<color name="notification_guts_priority_button_bg_fill_color_selected">@color/GM2_grey_800</color>
<color name="notification_guts_priority_button_bg_stroke_color">@color/GM2_grey_700</color>
- <color name="notification_guts_priority_button_bg_stroke_color_selected">@color/GM2_grey_700</color>
+ <color name="notification_guts_priority_button_bg_stroke_color_selected">@color/GM2_blue_300</color>
<color name="notification_section_header_label_color">@color/GM2_grey_200</color>
<color name="notification_section_clear_all_btn_color">@color/GM2_grey_500</color>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 26e4845..59cbdaa 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB-foutopsporing niet toegestaan"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"De gebruiker die momenteel is ingelogd op dit apparaat, kan USB-foutopsporing niet inschakelen. Als je deze functie wilt gebruiken, schakel je naar de primaire gebruiker."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB-poort uitgeschakeld"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"De USB-poort is uitgeschakeld en detecteert geen accessoires, zodat je apparaat wordt beschermd tegen vloeistof en vuil.\n\nJe ontvangt een melding wanneer je de USB-poort weer kunt gebruiken."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB-poort kan opladers en accessoires detecteren"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB inschakelen"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Meer informatie"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom om scherm te vullen"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Rek uit v. schermvulling"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Niet opnieuw weergeven"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Alles wissen"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Beheren"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Stille meldingen"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Alle stille meldingen wissen"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Meldingen onderbroken door \'Niet storen\'"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Nu starten"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Geen meldingen"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Blokkeren"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Blijven weergeven"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimaliseren"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Stil"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Stil blijven"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Waarschuwen"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Blijven waarschuwen"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Meldingen uitschakelen"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Meldingen van deze app blijven weergeven?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Vriendelijk"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Met prioriteit"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Helpt je focussen doordat meldingen alleen in het pull-downvenster worden weergegeven. Altijd stil."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Wordt weergegeven onder de prioriteitsmeldingen. Altijd stil."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Wordt weergegeven onder de prioriteitsmeldingen. Altijd stil."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Wordt weergegeven onder de prioriteitsmeldingen. Altijd stil."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Trekt de aandacht met geluid en een pictogram in de statusbalk. Wordt weergegeven op het vergrendelingsscherm."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Stil"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Waarschuwen"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Helpt je focussen zonder geluid of trilling."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Trekt je aandacht met geluid of trillingen."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Deze meldingen kunnen niet worden aangepast."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Deze groep meldingen kan hier niet worden geconfigureerd"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Melding via proxy"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Toestaan"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Weigeren"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Tikken om Batterijbesparing in te schakelen"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Inschakelen wanneer de batterij waarschijnlijk leeg raakt"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Nee"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Batterijbesparing is ingeschakeld"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Batterijbesparing wordt automatisch ingeschakeld wanneer de batterijstatus lager is dan <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Naar linksonder verplaatsen"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Naar rechtsonder verplaatsen"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Sluiten"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 1de3ea3..2aa9c968 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USBରେ ଡିବଗ୍ କରାଯାଇପାରିବ ନାହିଁ"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ସମ୍ପ୍ରତି ସାଇନ୍-ଇନ୍ କରିଥିବା ୟୁଜର୍ ଜଣକ ଏହି ଡିଭାଇସରେ USB ଡିବଗିଙ୍ଗ ଅନ୍ କରିପାରିବେ ନାହିଁ। ଏହି ବୈଶିଷ୍ଟ୍ୟ ବ୍ୟବହାର କରିବାକୁ, ପ୍ରାଥମିକ ୟୁଜର୍ରେ ସାଇନ୍-ଇନ୍ କରନ୍ତୁ।"</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB ପୋର୍ଟକୁ ଅକ୍ଷମ କରାଯାଇଛି"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"ଆପଣଙ୍କ ଡିଭାଇସ୍କୁ ତରଳ ପଦାର୍ଥ ଏବଂ ଧୂଳିରୁ ସୁରକ୍ଷିତ ରଖିବା ପାଇଁ, USB ପୋର୍ଟକୁ ଅକ୍ଷମ କରାଯାଇଛି ଏବଂ ଏହା କୌଣସି ଉପକରଣ ଚିହ୍ନଟ କରିବ ନାହିଁ। \n\n ଯେତେବେଳେ USB ପୋର୍ଟ ପୁଣିି ବ୍ୟବହାର କରିବାକୁ ସୁରକ୍ଷିତ ହେବ, ସେତେବେଳେ ଆପଣଙ୍କୁ ସୂଚିତ କରାଯିବ।"</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"ଚାର୍ଜର୍ ଏବଂ ଆକ୍ସେସରିଗୁଡ଼ିକୁ ଚିହ୍ନଟ କରିବାକୁ USB ପୋର୍ଟ ସକ୍ଷମ କରାଯାଇଛି"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB ସକ୍ଷମ କରନ୍ତୁ"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"ଅଧିକ ଜାଣନ୍ତୁ"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"ସ୍କ୍ରୀନ ଭରିବା ପାଇଁ ଜୁମ୍ କରନ୍ତୁ"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"ସ୍କ୍ରୀନ୍କୁ ଭରିବା ପାଇଁ ଟାଣନ୍ତୁ"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"ସ୍କ୍ରୀନଶଟ୍"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"ପୁଣି ଦେଖାନ୍ତୁ ନାହିଁ"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"ସମସ୍ତ ଖାଲି କରନ୍ତୁ"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"ପରିଚାଳନା କରନ୍ତୁ"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"ନୀରବ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"ସମସ୍ତ ନୀରବ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ଖାଲି କରନ୍ତୁ"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ବିକଳ୍ପ ଦ୍ୱାରା ବିଜ୍ଞପ୍ତି ପଜ୍ ହୋଇଛି"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"ବର୍ତ୍ତମାନ ଆରମ୍ଭ କରନ୍ତୁ"</string>
<string name="empty_shade_text" msgid="708135716272867002">"କୌଣସି ବିଜ୍ଞପ୍ତି ନାହିଁ"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"ବ୍ଲକ୍ କରନ୍ତୁ"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"ଦେଖାଇବା ଜାରି ରଖନ୍ତୁ"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"ଛୋଟ କରନ୍ତୁ"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"ନୀରବ"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"ନୀରବ ରହନ୍ତୁ"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"ଆଲର୍ଟ କରିବା"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"ଆଲର୍ଟ କରିବା ଜାରି ରଖନ୍ତୁ"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"ବିଜ୍ଞପ୍ତି ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"ଏହି ଆପ୍ରୁ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଦେଖାଇବା ଜାରି ରଖିବେ?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"ଧିରେ"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"ପ୍ରାଥମିକତା ଭିତ୍ତିକ"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"କେବଳ ପୁଲ୍-ଡାଉନ୍ ସେଡ୍ରେ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ଉପରେ ଫୋକସ୍ ରହିବା ପାଇଁ ଆପଣଙ୍କୁ ସାହାଯ୍ୟ କରିଥାଏ। ସର୍ବଦା ନୀରବ।"</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"ପ୍ରାଥମିକତା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ତଳେ ଡିସ୍ପ୍ଲେ ହୁଏ। ସର୍ବଦା ନିରବ।"</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"ପ୍ରାଥମିକତା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ତଳେ ଡିସ୍ପ୍ଲେ ହୁଏ। ସର୍ବଦା ନିରବ।"</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"ପ୍ରାଥମିକତା ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ତଳେ ଡିସ୍ପ୍ଲେ ହୁଏ। ସର୍ବଦା ନିରବ।"</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"ସାଉଣ୍ଡ୍ ଏବଂ ଏକ ଷ୍ଟାଟସ୍ ବାର୍ ଆଇକନ୍ ମାଧ୍ୟମରେ ଆପଣଙ୍କର ଧ୍ୟାନ ଆକର୍ଷିତ କରିଥାଏ। ଲକ୍ ସ୍କ୍ରିନ୍ରେ ଦେଖାଯାଇଥାଏ।"</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"ନୀରବ"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"ଆଲର୍ଟ କରିବା"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"ବିନା ସାଉଣ୍ଡ କିମ୍ବା ଭାଇବ୍ରେସନ୍ରେ ଆପଣଙ୍କୁ ଫୋକସ୍ କରିବାରେ ସାହାଯ୍ୟ କରେ।"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"ସାଉଣ୍ଡ କିମ୍ବା ଭାଇବ୍ରେସନ୍ ମାଧ୍ୟମରେ ଆପଣଙ୍କର ଧ୍ୟାନ ଆକର୍ଷିତ କରିଥାଏ।"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"ଏହି ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ପରିବର୍ତ୍ତନ କରିହେବ ନାହିଁ।"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"ଏଠାରେ ଏହି ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକର ଗ୍ରୁପ୍ କନଫ୍ୟୁଗର୍ କରାଯାଇପାରିବ ନାହିଁ"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"ବିଜ୍ଞପ୍ତି ପ୍ରକ୍ସୀ ହୋଇଛି"</string>
@@ -755,7 +746,7 @@
<string name="accessibility_data_saver_on" msgid="8454111686783887148">"ଡାଟା ସେଭର୍ ଅନ୍ ଅଛି"</string>
<string name="accessibility_data_saver_off" msgid="8841582529453005337">"ଡାଟା ସେଭର୍ ଅଫ୍ ଅଛି"</string>
<string name="switch_bar_on" msgid="1142437840752794229">"ଅନ୍"</string>
- <string name="switch_bar_off" msgid="8803270596930432874">"ଅଫ୍"</string>
+ <string name="switch_bar_off" msgid="8803270596930432874">"ବନ୍ଦ"</string>
<string name="nav_bar" msgid="1993221402773877607">"ନାଭିଗେଶନ୍ ବାର୍"</string>
<string name="nav_bar_layout" msgid="3664072994198772020">"ଲେଆଉଟ୍"</string>
<string name="left_nav_bar_button_type" msgid="8555981238887546528">"ସମ୍ପୂର୍ଣ୍ଣ ବାମ ବଟନ୍ ପ୍ରକାର"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"ଅସ୍ଵୀକାର କରନ୍ତୁ"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"ବ୍ୟାଟେରୀ ସେଭର୍ ଅନ୍ ହେବାର ସମୟ ସେଟ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"ବ୍ୟାଟେରୀ ସରିବାକୁ ଥିବା ସମୟରେ ଚାଲୁ କରନ୍ତୁ"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"ନାହିଁ, ଥାଉ"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"ଆଗରୁ ସେଟ୍ କରିଥିବା ସମୟ ଅନୁସାରେ ବ୍ୟାଟେରୀ ସେଭର୍ ଅନ୍ ହୋଇଛି"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"ଚାର୍ଜ <xliff:g id="PERCENTAGE">%d</xliff:g>%%ରୁ କମ୍ ହେଲେ ବ୍ୟାଟେରୀ ସେଭର୍ ଆପେ ଅନ୍ ହୋଇଯିବ।"</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"ତଳ ବାମକୁ ନିଅନ୍ତୁ"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"ତଳ ଡାହାଣକୁ ନିଅନ୍ତୁ"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"ଖାରଜ କରନ୍ତୁ"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index bb8bfb0..86c04d4 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB ਡਿਬੱਗਿੰਗ ਦੀ ਆਗਿਆ ਨਹੀਂ"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB ਪੋਰਟ ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਨੂੰ ਪਾਣੀ ਅਤੇ ਧੂੜ-ਮਿੱਟੀ ਤੋਂ ਬਚਾਉਣ ਲਈ, USB ਪੋਰਟ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ ਹੈ ਅਤੇ ਕੋਈ ਵੀ ਐਕਸੈਸਰੀ ਪਛਾਣੀ ਨਹੀਂ ਜਾਵੇਗੀ।\n\nUSB ਪੋਰਟ ਨੂੰ ਦੁਬਾਰਾ ਵਰਤਣਾ ਠੀਕ ਹੋਣ \'ਤੇ ਤੁਹਾਨੂੰ ਸੂਚਿਤ ਕੀਤਾ ਜਾਵੇਗਾ।"</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"ਚਾਰਜਰਾਂ ਅਤੇ ਉਪਸਾਧਨਾਂ ਦੀ ਪਛਾਣ ਕਰਨ ਲਈ USB ਪੋਰਟ ਚਾਲੂ ਹੈ"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB ਚਾਲੂ ਕਰੋ"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"ਹੋਰ ਜਾਣੋ"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"ਸਕ੍ਰੀਨ ਭਰਨ ਲਈ ਜ਼ੂਮ ਕਰੋ"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"ਸਕ੍ਰੀਨ ਭਰਨ ਲਈ ਸਟ੍ਰੈਚ ਕਰੋ"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਲਵੋ"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"ਦੁਬਾਰਾ ਨਾ ਦਿਖਾਓ"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"ਖਾਮੋਸ਼ ਸੂਚਨਾਵਾਂ"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"ਸਾਰੀਆਂ ਖਾਮੋਸ਼ ਸੂਚਨਾਵਾਂ ਕਲੀਅਰ ਕਰੋ"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਵੱਲੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਰੋਕਿਆ ਗਿਆ"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"ਹੁਣ ਚਾਲੂ ਕਰੋ"</string>
<string name="empty_shade_text" msgid="708135716272867002">"ਕੋਈ ਸੂਚਨਾਵਾਂ ਨਹੀਂ"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"ਬਲਾਕ ਕਰੋ"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"ਦਿਖਾਉਣਾ ਜਾਰੀ ਰੱਖੋ"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"ਛੋਟਾ ਕਰੋ"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"ਖਾਮੋਸ਼"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"ਚੁੱਪ ਰਹੋ"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"ਸੁਚੇਤਨਾ"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"ਸੁਚੇਤ ਰਖੋ"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"ਸੂਚਨਾਵਾਂ ਬੰਦ ਕਰੋ"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"ਕੀ ਇਸ ਐਪ ਤੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਦਿਖਾਉਣਾ ਜਾਰੀ ਰੱਖਣਾ ਹੈ?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"ਸਰਲ"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"ਤਰਜੀਹੀ"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"ਸਿਰਫ਼ ਹੇਠਾਂ ਖਿੱਚੀ ਜਾਣ ਵਾਲੀ ਸੂਚੀ ਵਿਚਲੀਆਂ ਸੂਚਨਾਵਾਂ ਨਾਲ ਧਿਆਨ ਦੇਣ ਵਿੱਚ ਤੁਹਾਡੀ ਮਦਦ ਕੀਤੀ ਜਾਂਦੀ ਹੈ। ਹਮੇਸ਼ਾਂ ਖਮੋਸ਼।"</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"ਘੱਟ ਤਰਜੀਹ ਵਾਲੀਆਂ ਸੂਚਨਾਵਾਂ ਦਿਖਾਓ। ਹਮੇਸ਼ਾਂ ਖਮੋਸ਼।"</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"ਘੱਟ ਤਰਜੀਹ ਵਾਲੀਆਂ ਸੂਚਨਾਵਾਂ ਦਿਖਾਓ। ਹਮੇਸ਼ਾਂ ਖਮੋਸ਼।"</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"ਘੱਟ ਤਰਜੀਹ ਵਾਲੀਆਂ ਸੂਚਨਾਵਾਂ ਦਿਖਾਓ। ਹਮੇਸ਼ਾਂ ਖਮੋਸ਼।"</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"ਧੁਨੀ ਅਤੇ ਸਥਿਤੀ ਪੱਟੀ ਪ੍ਰਤੀਕ ਨਾਲ ਤੁਹਾਡਾ ਧਿਆਨ ਖਿੱਚਦੀ ਹੈ। ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਖਾਓ।"</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"ਖਮੋਸ਼"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"ਸੁਚੇਤਨਾ"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"ਤੁਹਾਨੂੰ ਬਿਨਾਂ ਧੁਨੀ ਅਤੇ ਥਰਥਰਾਹਟ ਦੇ ਫੋਕਸ ਕਰਨ ਵਿੱਚ ਮਦਦ ਕਰਦਾ ਹੈ।"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"ਧੁਨੀ ਅਤੇ ਥਰਥਰਾਹਟ ਨਾਲ ਤੁਹਾਡਾ ਧਿਆਨ ਖਿੱਚਦੀ ਹੈ।"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"ਇਹਨਾਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਸੋਧਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ।"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"ਇਹ ਸੂਚਨਾਵਾਂ ਦਾ ਗਰੁੱਪ ਇੱਥੇ ਸੰਰੂਪਿਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"ਇੱਕ ਐਪ ਦੀ ਥਾਂ \'ਤੇ ਦੂਜੀ ਐਪ ਰਾਹੀਂ ਦਿੱਤੀ ਗਈ ਸੂਚਨਾ"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"ਕਰਨ ਦਿਓ"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"ਅਸਵੀਕਾਰ ਕਰੋ"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"ਬੈਟਰੀ ਸੇਵਰ ਦੀ ਸਮਾਂ-ਸੂਚੀ ਤਿਆਰ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"ਬੈਟਰੀ ਖਤਮ ਹੋਣ ਦੀ ਸੰਭਾਵਨਾ \'ਤੇ ਚਾਲੂ ਹੁੰਦਾ ਹੈ"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"ਬੈਟਰੀ ਸੇਵਰ ਸਮਾਂ-ਸੂਚੀ ਚਾਲੂ ਕੀਤੀ ਗਈ"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"ਬੈਟਰੀ ਦਾ ਪੱਧਰ <xliff:g id="PERCENTAGE">%d</xliff:g>%% ਤੋਂ ਘੱਟ ਹੋ ਜਾਣ \'ਤੇ ਬੈਟਰੀ ਸੇਵਰ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਚਾਲੂ ਹੋ ਜਾਵੇਗਾ।"</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"ਹੇਠਾਂ ਵੱਲ ਖੱਬੇ ਲਿਜਾਓ"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"ਹੇਠਾਂ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"ਖਾਰਜ ਕਰੋ"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index fdb4a3b..ec05500 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Debugowanie USB jest niedozwolone"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Użytkownik obecnie zalogowany na tym urządzeniu nie może włączyć debugowania USB. Aby użyć tej funkcji, przełącz się na użytkownika głównego."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Port USB wyłączony"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Aby chronić urządzenie przed wilgocią i zanieczyszczeniami, port USB został wyłączony i nie wykryje żadnych akcesoriów.\n\nOtrzymasz powiadomienie, gdy będzie można znów używać portu."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Port USB włączony, by wykrywać ładowarki i akcesoria"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Włącz USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Więcej informacji"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Powiększ, aby wypełnić ekran"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Rozciągnij, aby wypełnić ekran"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Zrzut ekranu"</string>
@@ -466,10 +464,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Nie pokazuj ponownie"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Ukryj wszystkie"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Zarządzaj"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Powiadomienia ciche"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Wyczyść wszystkie ciche powiadomienia"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Powiadomienia wstrzymane przez tryb Nie przeszkadzać"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Rozpocznij teraz"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Brak powiadomień"</string>
@@ -653,21 +649,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Zablokuj"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Pokazuj nadal"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimalizuj"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Bez dźwięku"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Zachowaj wyciszenie"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Alerty"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Powiadamiaj dalej"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Wyłącz powiadomienia"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Nadal pokazywać powiadomienia z tej aplikacji?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Subtelne"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Priorytetowe"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Pomaga Ci się skupić, wyświetlając powiadomienia tylko w obszarze powiadomień. Zawsze wyciszone."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Wyświetla pod powiadomieniami priorytetowymi. Zawsze wyciszone."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Wyświetla pod powiadomieniami priorytetowymi. Zawsze wyciszone."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Wyświetla pod powiadomieniami priorytetowymi. Zawsze wyciszone."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Zwraca Twoją uwagę dźwiękiem i ikoną na pasku stanu. Wyświetla na ekranie blokady."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Bez dźwięku"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Alert"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Pomaga Ci się skupić, nie sygnalizując niczego dźwiękiem ani wibracjami."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Przyciąga uwagę dźwiękiem lub wibracjami."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Tych powiadomień nie można zmodyfikować."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Tej grupy powiadomień nie można tu skonfigurować"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Powiadomienie w zastępstwie"</string>
@@ -920,8 +911,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Zezwól"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Odmów"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Kliknij, by zaplanować działanie oszczędzania baterii"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Oszczędzanie baterii włącza się, jeśli bateria jest prawie wyczerpana"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Nie"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Harmonogram oszczędzania baterii jest aktywny"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Oszczędzanie baterii włączy się automatycznie, gdy poziom naładowania baterii spadnie poniżej <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -954,4 +944,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Przenieś w lewy dolny róg"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Przenieś w prawy dolny róg"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Zamknij"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 6edbced..a7d85ab 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Depuração USB não permitida"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"O usuário conectado a este dispositivo não pode ativar a depuração USB. Para usar esse recurso, mude para o usuário principal \"NAME\"."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Porta USB desativada"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Para proteger seu dispositivo de líquidos e detritos, a porta USB está desativada e não detectará nenhum acessório.\n\nVocê receberá uma notificação quando for seguro usar a porta USB novamente."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Porta USB ativada para detectar carregadores e acessórios"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Ativar USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Saiba mais"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom p/ preencher a tela"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Ampliar p/ preencher tela"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Captura de tela"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Não mostrar novamente"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Limpar tudo"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Gerenciar"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Notificações silenciosas"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Apagar todas as notificações silenciosas"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notificações pausadas pelo modo \"Não perturbe\""</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Iniciar agora"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Sem notificações"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Bloquear"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Continuar mostrando"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizar"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Silencioso"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Continuar sem som"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Alertar"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Continuar alertando"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Desativar notificações"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuar mostrando notificações desse app?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Discreta"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Priorizada"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Ajuda você a se concentrar, já que as notificações são exibidas apenas na aba suspensa. Sempre silenciosa."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Exibida abaixo das notificações prioritárias. Sempre silenciosa."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Exibida abaixo das notificações prioritárias. Sempre silenciosa."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Exibida abaixo das notificações prioritárias. Sempre silenciosa."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Chama sua atenção com sons e um ícone na barra de status. Exibida na tela de bloqueio."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Silenciosa"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Alertar"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Ajuda você a manter o foco sem som ou vibração."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Chama sua atenção com som ou vibração."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Não é possível modificar essas notificações."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Não é possível configurar esse grupo de notificações aqui"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Notificação salva no proxy"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Permitir"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Negar"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Toque para programar o recurso Economia de bateria"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Ativada quando há possibilidade de a bateria acabar"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Não"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Programação do recurso Economia de bateria ativada"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"O recurso Economia de bateria será ativado automaticamente depois que a bateria ficar abaixo de <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Mover para canto inferior esquerdo"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Mover para canto inferior direito"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Dispensar"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index e373b3b..4260dc2 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Depuração USB não permitida"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"O utilizador com sessão iniciada atualmente neste dispositivo não pode ativar a depuração USB. Para utilizar esta funcionalidade, mude para o utilizador principal."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Porta USB desativada"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Para proteger o dispositivo contra líquidos ou resíduos, a porta USB está desativada e não irá detetar quaisquer acessórios.\n\nSerá notificado quando for seguro utilizar a porta USB novamente."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Porta USB ativada para detetar carregadores e acessórios"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Ativar USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Saiba mais"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom para preencher o ecrã"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Esticar p. caber em ec. int."</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Captura de ecrã"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Não mostrar de novo"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Limpar tudo"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Gerir"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Notificações silenciosas"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Limpar todas as notificações silenciosas"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notificações colocadas em pausa pelo modo Não incomodar."</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Começar agora"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Sem notificações"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Bloquear"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Continuar a mostrar"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizar"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Silencioso"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Continuar sem som"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Alertar"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Continuar a alertar"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Desativar notificações"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Pretende continuar a ver notificações desta aplicação?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Discretas"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioritárias"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Ajuda-o a concentrar-se com notificações apenas no painel pendente. Sempre silenciosa."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"É apresentada abaixo das notificações prioritárias. Sempre silenciosa."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"É apresentada abaixo das notificações prioritárias. Sempre silenciosa."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Apresentadas abaixo das notificações prioritárias. Sempre silenciosas."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Chamam à atenção com som e ícones na barra de estado. Apresentadas no ecrã de bloqueio."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Silencioso"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Alertar"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Ajuda-o a focar-se sem som ou vibração."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Chama a sua atenção com som ou vibração."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Não é possível modificar estas notificações."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Não é possível configurar este grupo de notificações aqui."</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Notificação de aplicação proxy"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Permitir"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Recusar"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Tocar para agendar a Poupança de bateria"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Ativar quando for provável que a bateria se esgote"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Não, obrigado"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Poupança de bateria agendada ativada"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"A Poupança de bateria é ativada automaticamente quando o nível de bateria está abaixo de <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Mover p/ parte infer. esquerda"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Mover parte inferior direita"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Ignorar"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 6edbced..a7d85ab 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Depuração USB não permitida"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"O usuário conectado a este dispositivo não pode ativar a depuração USB. Para usar esse recurso, mude para o usuário principal \"NAME\"."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Porta USB desativada"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Para proteger seu dispositivo de líquidos e detritos, a porta USB está desativada e não detectará nenhum acessório.\n\nVocê receberá uma notificação quando for seguro usar a porta USB novamente."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Porta USB ativada para detectar carregadores e acessórios"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Ativar USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Saiba mais"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom p/ preencher a tela"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Ampliar p/ preencher tela"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Captura de tela"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Não mostrar novamente"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Limpar tudo"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Gerenciar"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Notificações silenciosas"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Apagar todas as notificações silenciosas"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notificações pausadas pelo modo \"Não perturbe\""</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Iniciar agora"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Sem notificações"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Bloquear"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Continuar mostrando"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizar"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Silencioso"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Continuar sem som"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Alertar"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Continuar alertando"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Desativar notificações"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Continuar mostrando notificações desse app?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Discreta"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Priorizada"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Ajuda você a se concentrar, já que as notificações são exibidas apenas na aba suspensa. Sempre silenciosa."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Exibida abaixo das notificações prioritárias. Sempre silenciosa."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Exibida abaixo das notificações prioritárias. Sempre silenciosa."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Exibida abaixo das notificações prioritárias. Sempre silenciosa."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Chama sua atenção com sons e um ícone na barra de status. Exibida na tela de bloqueio."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Silenciosa"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Alertar"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Ajuda você a manter o foco sem som ou vibração."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Chama sua atenção com som ou vibração."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Não é possível modificar essas notificações."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Não é possível configurar esse grupo de notificações aqui"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Notificação salva no proxy"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Permitir"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Negar"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Toque para programar o recurso Economia de bateria"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Ativada quando há possibilidade de a bateria acabar"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Não"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Programação do recurso Economia de bateria ativada"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"O recurso Economia de bateria será ativado automaticamente depois que a bateria ficar abaixo de <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Mover para canto inferior esquerdo"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Mover para canto inferior direito"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Dispensar"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index db853a0..31709b6 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Remedierea erorilor prin USB nu este permisă"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Utilizatorul conectat momentan pe acest dispozitiv nu poate activa remedierea erorilor prin USB. Pentru a folosi această funcție, comutați la utilizatorul principal."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Portul USB a fost dezactivat"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Pentru a vă proteja dispozitivul de lichide sau reziduuri, portul USB este dezactivat și nu va detecta niciun accesoriu.\n\nVeți primi o notificare când puteți folosi din nou portul USB."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Portul USB a fost activat pentru a detecta încărcătoarele și accesoriile"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Activați USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Mai multe"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zoom pt. a umple ecranul"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Înt. pt. a umple ecranul"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Instantaneu"</string>
@@ -461,10 +459,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Nu se mai afișează"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Ștergeți toate notificările"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Gestionați"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Notificări silențioase"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Ștergeți toate notificările silențioase"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Notificări întrerupte prin „Nu deranja”"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Începeți acum"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Nicio notificare"</string>
@@ -648,21 +644,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Blocați"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Continuați afișarea"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizați"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Silențios"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Păstrați modul silențios"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Alertare"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Păstrați alerta"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Dezactivați notificările"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Doriți să continuați afișarea notificărilor de la această aplicație?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Discret"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Cu prioritate"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Vă ajută să vă concentrați cu notificări doar în fereastra trasă în jos. Întotdeauna silențios."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Se afișează sub notificările prioritare. Întotdeauna silențios."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Se afișează sub notificările prioritare. Întotdeauna silențios."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Se afișează sub notificările prioritare. Întotdeauna silențios."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Vă atrage atenția cu sunete și o pictogramă în bara de stare. Se afișează pe ecranul de blocare."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Silențios"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Alertare"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Vă ajută să vă concentrați fără sunet sau vibrare."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Vă atrage atenția fără sunet sau vibrare."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Aceste notificări nu pot fi modificate."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Acest grup de notificări nu poate fi configurat aici"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Notificare prin proxy"</string>
@@ -913,8 +904,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Permiteți"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Refuzați"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Atingeți pentru a programa Economisirea energiei"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Porniți dacă este probabil ca bateria să se descarce"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Nu, mulțumesc"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"S-a activat programarea pentru Economisirea bateriei"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Economisirea bateriei se va activa automat imediat ce bateria scade sub <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -947,4 +937,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Mutați în stânga jos"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Mutați în dreapta jos"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Închideți"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 98b1868..6c926f1 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Отладка по USB запрещена"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"В этом аккаунте нельзя включить отладку по USB. Перейдите в аккаунт основного пользователя."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB-порт отключен"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Чтобы внутрь устройства не попала вода или грязь, USB-порт был отключен. Сейчас через него нельзя подсоединять другие устройства.\n\nКогда USB-порт снова можно будет использовать, вы получите уведомление."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB-порт активен и может распознавать аксессуары и зарядные устройства."</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Включить USB-порт"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Подробнее"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Подогнать по размерам экрана"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Растянуть на весь экран"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Скриншот"</string>
@@ -464,10 +462,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Больше не показывать"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Очистить все"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Настроить"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Беззвучные уведомления"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Отклонить все беззвучные уведомления"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"В режиме \"Не беспокоить\" уведомления заблокированы"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Начать"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Нет уведомлений"</string>
@@ -651,21 +647,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Заблокировать"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Показывать"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Свернуть"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Без звука"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Не включать звук"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Присылать оповещения"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Присылать уведомления"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Выключить уведомления"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Показывать уведомления от этого приложения?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Беззвучные"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Приоритетные"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Уведомления появляются только на специальной панели, чтобы вы не отвлекались. Всегда без звука."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Показ уведомлений с низким приоритетом. Всегда без звука."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Показ уведомлений с низким приоритетом. Всегда без звука."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Показ уведомлений с низким приоритетом. Всегда без звука."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Со звуком и значком в строке состояния. Появляются на заблокированном экране."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Без звука"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Оповещения"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Уведомления приходят без звука и вибрации"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Уведомления приходят со звуком или вибрацией"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Эти уведомления нельзя изменить."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Эту группу уведомлений нельзя настроить здесь."</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Уведомление отправлено через прокси-сервер."</string>
@@ -918,8 +909,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Да"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Нет"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Нажмите, чтобы настроить режим энергосбережения"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Включать, если высока вероятность, что батарея скоро разрядится"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Отмена"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Автоматический переход в режим энергосбережения включен"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Режим энергосбережения активируется при заряде батареи ниже <xliff:g id="PERCENTAGE">%d</xliff:g> %%."</string>
@@ -952,4 +942,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Перенести в левый нижний угол"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Перенести в правый нижний угол"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Закрыть"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index a2be259..aee39fd 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -647,13 +647,10 @@
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"අඟවමින් සිටින්න"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"දැනුම්දීම් අක්රිය කරන්න"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"මෙම යෙදුම වෙතින් දැනුම්දීම් පෙන්වමින් තබන්නද?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"මෘදු"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"ප්රමුඛ කළ"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"ඔබට පහළට-ඇදීමේ වැස්මේ පමණක් දැනුම්දීම් සමඟ අවධානය යොමු කිරීමට උදවු කරයි. සැම විටම නිහඬයි."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"ප්රමුඛතා දැනුම්දීම්වලට පහළින් සංදර්ශනය වේ. සැම විටම නිහඬයි."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"ප්රමුඛතා දැනුම්දීම්වලට පහළින් සංදර්ශනය වේ. සැම විටම නිහඬයි."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"ප්රමුඛතා දැනුම්දීම්වලට පහළින් සංදර්ශනය වේ. සැම විටම නිහඬයි."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"ශබ්දයක් සහ තත්ත්ව තීරු නිරූපකයක් සමඟින් ඔබේ අවධානය ලබා ගනී. අගුළු තිරයෙහි පෙන්වයි."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"නිහඬ"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"ඇඟවීම"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"ඔබට ශබ්දය හෝ කම්පනය නොමැතිව අවධානය යොමු කිරීමට උදවු කරයි."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"ශබ්දය හෝ කම්පනය සමඟ ඔබේ අවධානය ලබා ගනී."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"මෙම දැනුම්දීම් වෙනස් කළ නොහැක."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"මෙම දැනුම්දීම් සමූහය මෙහි වින්යාස කළ නොහැක"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"ප්රොක්සි කළ දැනුම්දීම"</string>
@@ -935,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"පහළ වමට ගෙන යන්න"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"පහළ දකුණට ගෙන යන්න"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"ඉවතලන්න"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index f4712e3..6bb9dfe 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Ladenie cez USB nie je povolené"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Používateľ, ktorý je práve prihlásený v tomto zariadení, nemôže zapnúť ladenie USB. Ak chcete použiť túto funkciu, prepnite na hlavného používateľa."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Port USB je deaktivovaný"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Port USB je deaktivovaný na zaistenie ochrany zariadenia pred tekutinami alebo nečistotami a nerozpoznáva príslušenstvo.\n\nKeď ho budete môcť znova použiť, upozorníme vás."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Bol povolený port USB na zisťovanie nabíjačiek a príslušenstva"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Povoliť USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Ďalšie informácie"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Priblížiť na celú obrazovku"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Na celú obrazovku"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Snímka"</string>
@@ -313,7 +311,7 @@
<string name="quick_settings_bluetooth_secondary_label_audio" msgid="5673845963301132071">"Zvuk"</string>
<string name="quick_settings_bluetooth_secondary_label_headset" msgid="1880572731276240588">"Náhlavná súprava"</string>
<string name="quick_settings_bluetooth_secondary_label_input" msgid="2173322305072945905">"Vstup"</string>
- <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="4930931771490695395">"Načúvacie pomôcky"</string>
+ <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="4930931771490695395">"Načúvadlá"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="4551281899312150640">"Zapína sa…"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Jas"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automatické otáčanie"</string>
@@ -464,10 +462,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Nabudúce nezobrazovať"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Vymazať všetko"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Spravovať"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Tiché upozornenia"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Vymazať všetky tiché upozornenia"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Upozornenia sú pozastavené režimom bez vyrušení"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Spustiť"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Žiadne upozornenia"</string>
@@ -651,21 +647,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Blokovať"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Naďalej zobrazovať"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimalizovať"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Tichý"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Naďalej upozorňovať potichu"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Upozorňovanie"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Naďalej upozorňovať"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Vypnúť upozornenia"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Majú sa upozornenia z tejto aplikácie naďalej zobrazovať?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Nenápadné"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioritné"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Pomáha sústrediť sa zobrazovaním upozornení iba v rozbaľovacom paneli. Vždy potichu."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Zobrazuje sa pod prioritnými upozorneniami. Vždy potichu."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Zobrazuje sa pod prioritnými upozorneniami. Vždy potichu."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Zobrazuje sa pod prioritnými upozorneniami. Vždy potichu."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Upúta zvukom a zobrazením ikony v stavovom riadku. Zobrazuje sa na uzamknutej obrazovke."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Tiché"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Varovné"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Pomáha vám sústrediť sa bez zvukov či vibrácií."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Upúta vás zvukom alebo vibráciami."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Tieto upozornenia sa nedajú upraviť."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Túto skupinu upozornení nejde na tomto mieste konfigurovať"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Približné upozornenie"</string>
@@ -918,8 +909,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Povoliť"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Zamietnuť"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Klepnutím naplánujete aktivovanie Šetriča batérie"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Zapnite, keď je batéria takmer vybitá"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Nie, vďaka"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Plánované aktivovanie Šetriča batérie bolo zapnuté"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Keď batéria klesne pod <xliff:g id="PERCENTAGE">%d</xliff:g> %%, automaticky sa aktivujte Šetrič batérie."</string>
@@ -952,4 +942,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Presunúť doľava nadol"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Presunúť doprava nadol"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Zavrieť"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 58ff074..efd73d8 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Odpravljanje napak s povezavo USB ni dovoljeno"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Uporabnik, trenutno prijavljen v napravo, ne more vklopiti odpravljanja napak s povezavo USB. Če želite uporabljati to funkcijo, preklopite na primarnega uporabnika."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Vrata USB so onemogočena"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Zaradi zaščite naprave pred tekočino ali umazanijo so vrata USB onemogočena in ne bodo zaznala nobene dodatne opreme.\n\nKo bo znova varno uporabljati vrata USB, boste obveščeni."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Vrata USB so omogočena za zaznavanje polnilnikov in dodatne opreme"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Omogoči USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Več o tem"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Povečava čez cel zaslon"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Raztegnitev čez zaslon"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Posnetek"</string>
@@ -464,10 +462,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Tega ne prikaži več"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Izbriši vse"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Upravljanje"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Tiha obvestila"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Brisanje vseh tihih obvestil"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Prikazovanje obvestil je začasno zaustavljeno z načinom »ne moti«"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Začni zdaj"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Ni obvestil"</string>
@@ -651,21 +647,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Blokiraj"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Prikazuj še naprej"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimiraj"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Tiho"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Še naprej prikazuj brez zvoka"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Z zvočnim opozorilom"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Še naprej opozarjaj"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Izklopi obvestila"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Želite, da so obvestila te aplikacije še naprej prikazana?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Diskretno"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prednostno"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Nemoteč prikaz samo na poteznem zaslonu z obvestili. Vedno tiho."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Prikaz pod prednostnimi obvestili. Vedno tiho."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Prikaz pod prednostnimi obvestili. Vedno tiho."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Prikaz pod prednostnimi obvestili. Vedno tiho."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Opozarjanje z zvokom in ikono v vrstici stanja. Prikaz na zaklenjenem zaslonu."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Tiho"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Z zvočnim opozorilom"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Nemoteč prikaz brez zvoka ali vibriranja"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Pritegnitev pozornosti z zvokom ali vibriranjem"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Za ta obvestila ni mogoče spremeniti nastavitev."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Te skupine obvestil ni mogoče konfigurirati tukaj"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Posredovano obvestilo"</string>
@@ -918,8 +909,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Dovoli"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Zavrni"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Dotaknite se za načrtovanje varčevanja z energijo akumulatorja"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Vklop, če je verjetno, da se bo akumulator izpraznil"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Ne, hvala"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Urnik varčevanja z energijo akumulatorja je vklopljen"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Varčevanje z energijo akumulatorja se bo samodejno vklopilo, ko bo energija akumulatorja pod <xliff:g id="PERCENTAGE">%d</xliff:g> %%."</string>
@@ -952,4 +942,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Premakni spodaj levo"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Premakni spodaj desno"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Opusti"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 592915b..091c540 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Korrigjimi i USB-së nuk lejohet"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Përdoruesi i identifikuar aktualisht në këtë pajisje nuk mund ta aktivizojë korrigjimin e USB-së. Për ta përdorur këtë funksion, kalo te përdoruesi parësor."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Porta e USB-së është çaktivizuar"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Për të mbrojtur pajisjen tënde nga lëngjet apo papastërtitë, porta e USB-së është çaktivizuar dhe nuk do t\'i dallojë aksesorët.\n\nDo të njoftohesh kur të mos jetë problem përdorimi përsëri i portës USB."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Porta USB është aktivizuar për të zbuluar karikuesit dhe aksesorët"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Aktivizo USB-në"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Mëso më shumë"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zmadho për të mbushur ekranin"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Shtrije për të mbushur ekranin"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Pamja e ekranit"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Mos e shfaq sërish"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Pastroji të gjitha"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Menaxho"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Njoftimet në heshtje"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Pastro të gjitha njoftimet në heshtje"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Njoftimet janë vendosur në pauzë nga modaliteti \"Mos shqetëso\""</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Fillo tani"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Asnjë njoftim"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Blloko"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Vazhdo të shfaqësh"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimizo"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Në heshtje"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Qëndro në heshtje"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Sinjalizimi"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Vazhdo të sinjalizosh"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Çaktivizo njoftimet"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Do të vazhdosh t\'i shfaqësh njoftimet nga ky aplikacion?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Me rëndësi të ulët"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Me prioritet"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Të ndihmon të përqendrohesh me njoftimet vetëm në panelin zbritës. Gjithmonë në heshtje."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Shfaqet nën njoftimet me përparësi. Gjithmonë në heshtje."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Shfaqet nën njoftimet me përparësi. Gjithmonë në heshtje."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Shfaqet nën njoftimet me përparësi. Gjithmonë në heshtje."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Të tërheq vëmendjen me tingull dhe një ikonë në shiritin e statusit. Shfaqet në ekranin e kyçjes."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Në heshtje"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Sinjalizimi"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Të ndihmon të fokusohesh pa tinguj ose dridhje."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Të tërheq vëmendjen me tinguj ose dridhje."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Këto njoftime nuk mund të modifikohen."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Ky grup njoftimesh nuk mund të konfigurohet këtu"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Njoftim i dërguar me përfaqësues"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Lejo"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Refuzo"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Trokit për të planifikuar \"Kursyesin e baterisë\""</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Aktivizoje kur bateria mund të mbarojë"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Jo"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Planifikimi i \"Kursyesit të baterisë\" është aktivizuar"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"\"Kursyesi i baterisë\" do të aktivizohet automatikisht kur bateria të jetë nën <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Zhvendos poshtë majtas"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Lëvize poshtë djathtas"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Hiq"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index c71bbf6..0298677 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Отклањање грешака на USB-у није дозвољено"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Корисник који је тренутно пријављен на овај уређај не може да укључи отклањање грешака на USB-у. Да бисте користили ову функцију, пребаците на примарног корисника."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB порт је онемогућен"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Да би се уређај заштитио од течности или нечистоће, USB порт је онемогућен и неће откривати додатну опрему.\n\nОбавестићемо вас када поново будете могли да користите USB порт."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB порт је омогућен ради откривања пуњача и додатне опреме"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Омогући USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Сазнајте више"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Зумирај на целом екрану"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Развуци на цео екран"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Снимак екрана"</string>
@@ -461,10 +459,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Не приказуј поново"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Обриши све"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Управљајте"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Нечујна обавештења"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Обришите сва нечујна обавештења"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Обавештења су паузирана режимом Не узнемиравај"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Започни одмах"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Нема обавештења"</string>
@@ -648,21 +644,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Блокирај"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Настави да приказујеш"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Умањи"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Нечујно"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Не укључуј звук"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Упозоравање"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Настави са обавештењима"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Искључи обавештења"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Желите ли да се обавештења из ове апликације и даље приказују?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Дискретно"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Приоритетно"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Помаже вам да се фокусирате. Обавештења су само на падајућој траци. Увек нечујно."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Приказује се испод приоритетних обавештења. Увек нечујно."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Приказује се испод приоритетних обавештења. Увек нечујно."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Приказује се испод приоритетних обавештења. Увек нечујно."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Привлачи вам пажњу помоћу звука и иконе статусне траке. Приказује се на закључаном екрану."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Нечујно"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Упозоравање"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Помаже вам да се концентришете без звука или вибрације."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Привлачи вам пажњу помоћу звука или вибрације."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Ова обавештења не могу да се мењају."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Ова група обавештења не може да се конфигурише овде"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Обавештење преко проксија"</string>
@@ -913,8 +904,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Дозволи"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Одбиј"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Додирните да бисте направили распоред за уштеду батерије"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Укључите ако ће батерија вероватно да се испразни"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Не, хвала"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Распоред за уштеду батерије је укључен"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Уштеда батерије ће се аутоматски укључивати када батерија падне испод <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -947,4 +937,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Премести доле лево"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Премести доле десно"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Одбаци"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 0f39fc2..cca609b 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB-felsökning är inte tillåtet"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Användaren som är inloggad på enheten för närvarande kan inte aktivera USB-felsökning. Byt till den primära användaren om du vill använda den här funktionen."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB-porten har inaktiverats"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"USB-porten har inaktiverats för att skydda enheten mot vätska eller smuts. Inga tillbehör kommer att hittas.\n\nDu meddelas när det går att använda USB-porten igen."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB-porten har aktiverats för identifiering av laddare och tillbehör"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Aktivera USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Läs mer"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Zooma för att fylla skärm"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Dra för att fylla skärmen"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Skärmdump"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Visa inte igen"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Rensa alla"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Hantera"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Ljudlösa aviseringar"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Rensa alla ljudlösa aviseringar"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Aviseringar har pausats via Stör ej"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Starta nu"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Inga aviseringar"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Blockera"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Fortsätt visa"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Minimera"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Ljudlöst"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Fortsätt visa utan ljud"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Med avisering"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Fortsätt meddela"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Inaktivera aviseringar"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Vill du fortsätta visa aviseringar för den här appen?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Utan avbrott"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Prioriterade"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Aviseringar visas bara på aviseringspanelen så att du kan fokusera på det viktigaste. Alltid tyst."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Aviseringar med låg prioritet visas. Alltid tyst."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Aviseringar med låg prioritet visas. Alltid tyst."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Aviseringar med låg prioritet visas. Alltid tyst."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Fångar din uppmärksamhet med ljud och en ikon i statusfältet. Visas på låsskärmen."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Tyst"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Påkallar uppmärksamhet"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Inga ljud eller vibrationer som stör koncentrationen."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Påkallar uppmärksamhet med ljud eller vibrationer."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Det går inte att ändra de här aviseringarna."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Den här aviseringsgruppen kan inte konfigureras här"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Avisering via proxy"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Tillåt"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Neka"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Tryck för att skapa ett schema för batterisparläget"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Aktivera när batteriet håller på att ta slut"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Nej tack"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Schema för Batterisparläge aktiverat"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Batterisparläget aktiveras automatiskt när batterinivån är under <xliff:g id="PERCENTAGE">%d</xliff:g> %%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Flytta längst ned till vänster"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Flytta längst ned till höger"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Stäng"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index d91ba6d..b4177fc 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Utatuzi wa USB hauruhusiwi"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Mtumiaji aliyeingia katika akaunti kwa kutumia kifaa hiki kwa sasa hawezi kuwasha utatuzi wa USB. Ili utumie kipengele hiki, tumia akaunti ya mtumiaji wa msingi."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Mlango wa USB umezimwa"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Ili ulinde kifaa chako dhidi ya vitu vyenye unyevu au uchafu, mlango wa USB umezimwa na hautatambua vifaa vyovyote.\n\nUtaarifiwa itapokuwa sawa kutumia mlango wa USB tena."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Mlango wa USB umewezeshwa ili utambue chaja na vifuasi"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Washa kipengele cha USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Pata maelezo zaidi"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Kuza ili kujaza skrini"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Tanua ili kujaza skrini"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Pichaskrini"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Usionyeshe tena"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Futa zote"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Dhibiti"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Arifa zisizo na sauti"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Futa arifa zote zisizo na sauti"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Kipengele cha Usinisumbue kimesitisha arifa"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Anza sasa"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Hakuna arifa"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Zuia"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Endelea kuonyesha"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Punguza"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Kimya"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Isitoe sauti"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Kutoa arifa"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Endelea kutoa arifa"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Zima arifa"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Ungependa kuendelea kuonyesha arifa kutoka programu hii?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Bila sauti"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Zilizopewa kipaumbele"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Hukusaidia kuangazia arifa pekee kwenye orodha kunjuzi. Kimya kila wakati."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Huonyeshwa chini ya arifa za kipaumbele. Kimya kila wakati."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Huonyeshwa chini ya arifa za kipaumbele. Kimya kila wakati."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Huonyeshwa chini ya arifa za kipaumbele. Kimya kila wakati."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Hupata umakinifu wako kwa sauti na aikoni ya sehemu ya arifa. Huonyeshwa kwenye skrini iliyofungwa."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Kimya"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Kutoa arifa"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Hukusaidia kuwa makini bila sauti au mtetemo."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Hupata umakinifu wako kwa sauti na mtetemo."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Arifa hizi haziwezi kubadilishwa."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Kikundi hiki cha arifa hakiwezi kuwekewa mipangilio hapa"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Arifa wakilishi"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Ruhusu"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Kataa"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Gusa ili uratibu wakati wa kuwasha Kiokoa Betri"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Washa wakati betri inakaribia kuisha"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Hapana, asante"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Ratiba ya Kiokoa Betri imewashwa"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Kiokoa Betri kitawaka kiotomatiki baada ya chaji ya betri kufika chini ya <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Sogeza chini kushoto"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Sogeza chini kulia"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Ondoa"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 172b62c..bfca476 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB பிழைதிருத்தம் அனுமதிக்கப்படவில்லை"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"தற்போது இந்தச் சாதனத்தில் உள்நுழைந்துள்ள பயனரால் USB பிழைதிருத்தத்தை இயக்க முடியாது. இந்த அம்சத்தை இயக்க, முதன்மைப் பயனருக்கு மாறவும்."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB போர்ட் முடக்கப்பட்டது"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"தேவையற்றவையில் இருந்து உங்கள் சாதனத்தைப் பாதுகாக்க USB போர்ட் முடக்கப்பட்டுள்ளது. மேலும் எந்தத் துணைக் கருவிகளையும் அது கண்டறியாது.\n\nUSB போர்ட்டை மீண்டும் எப்போது பயன்படுத்தலாம் என்பதைப் பற்றி உங்களுக்குத் தெரிவிக்கப்படும்."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"சார்ஜர்களையும் துணைக்கருவிகளையும் கண்டறிவதற்காக USB போர்ட் இயக்கப்பட்டுள்ளது"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USBயை இயக்கு"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"மேலும் அறிக"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"திரையை நிரப்ப அளவை மாற்று"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"திரையை நிரப்ப இழு"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"ஸ்கிரீன்ஷாட்"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"மீண்டும் காட்டாதே"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"எல்லாவற்றையும் அழி"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"அறிவிப்புகளை நிர்வகி"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"ஒலியில்லாத அறிவிப்புகள்"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"ஒலியில்லாத அழைப்புகள் அனைத்தையும் அழிக்கும்"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"\'தொந்தரவு செய்ய வேண்டாம்\' அம்சத்தின் மூலம் அறிவிப்புகள் இடைநிறுத்தப்பட்டுள்ளன"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"இப்போது தொடங்கு"</string>
<string name="empty_shade_text" msgid="708135716272867002">"அறிவிப்புகள் இல்லை"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"தடு"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"அறிவிப்புகளைத் தொடர்ந்து காட்டு"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"சிறிதாக்கு"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"நிசப்தம்"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"அறிவிப்புகளை ஒலியின்றிக் காட்டு"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"விழிப்பூட்டல்"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"தொடர்ந்து விழிப்பூட்டு"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"அறிவிப்புகளை முடக்கு"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"இந்த ஆப்ஸின் அறிவிப்புகளைத் தொடர்ந்து காட்டவா?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"ஜென்டில்"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"முன்னுரிமைப்படுத்தப்பட்டது"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"கீழ் இழுக்கும் ஷேடில் வரும் அறிவிப்புகளில் மட்டும் கவனம் செலுத்த உதவுகிறது. எப்போதும் நிசப்தம்."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"குறைந்த முன்னுரிமைப் பெற்ற அறிவிப்புகளைக் காட்டும். எப்போதும் நிசப்தம்."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"குறைந்த முன்னுரிமைப் பெற்ற அறிவிப்புகளைக் காட்டும். எப்போதும் நிசப்தம்."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"குறைந்த முன்னுரிமைப் பெற்ற அறிவிப்புகளைக் காட்டும். எப்போதும் நிசப்தம்."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"ஒலி & நிலைப் பட்டி ஐகான் மூலம் உங்கள் கவனத்தைப் பெறுகிறது. பூட்டுத் திரையில் காட்டும்."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"நிசப்தம்"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"விழிப்பூட்டல்"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"ஒலியோ அதிர்வோ இல்லாமல் முழு கவனம் செலுத்த உதவும்."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"ஒலியோ அதிர்வோ ஏற்படுத்தி உங்கள் கவனத்தை ஈர்க்கும்."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"இந்த அறிவிப்புகளை மாற்ற இயலாது."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"இந்த அறிவுப்புக் குழுக்களை இங்கே உள்ளமைக்க இயலாது"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"ப்ராக்ஸியான அறிவிப்பு"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"அனுமதி"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"நிராகரி"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"பேட்டரி சேமிப்பானை ஆன் செய்வது தொடர்பாகத் திட்டமிட, தட்டவும்"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"பேட்டரி தீர்ந்துபோகும் நிலையில் இருக்கும் போது ஆன் செய்யப்படும்"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"வேண்டாம்"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"திட்டமிட்ட பேட்டரி சேமிப்பான் ஆன் செய்யப்பட்டது"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"பேட்டரியின் அளவு <xliff:g id="PERCENTAGE">%d</xliff:g>%%க்குக் கீழ் குறையும்போது, பேட்டரி சேமிப்பான் தானாகவே ஆன் செய்யப்படும்."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"கீழே இடப்புறமாக நகர்த்து"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"கீழே வலதுபுறமாக நகர்த்து"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"மூடுக"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 033fce4..657e577 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB డీబగ్గింగ్కి అనుమతి లేదు"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ఈ పరికరానికి ప్రస్తుతం సైన్ ఇన్ చేసిన వినియోగదారు USB డీబగ్గింగ్ ఆన్ చేయలేరు. ఈ ఫీచర్ ఉపయోగించడానికి, ప్రాథమిక వినియోగదారుకి మారాలి."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB పోర్ట్ నిలిపివేయబడింది"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"మీ పరికరంలోకి నీరు లేదా చెత్తాచెదారం చేరిపోకుండా కాపాడటానికి, USB పోర్ట్ నిలిపివేయబడుతుంది, అలాగే యాక్సెసరీలు వేటిని గుర్తించదు.\n\nUSB పోర్ట్ను ఉపయోగించడం సురక్షితమేనని నిర్ధారించుకున్న తర్వాత, మళ్లీ మీకో నోటిఫికేషన్ రూపంలో తెలియజేయబడుతుంది."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"ఛార్జర్లు, యాక్సెసరీలను గుర్తించే విధంగా USB పోర్ట్ ప్రారంభించబడింది"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USBని ప్రారంభించు"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"మరింత తెలుసుకోండి"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"స్క్రీన్కు నింపేలా జూమ్ చేయండి"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"స్క్రీన్కు నింపేలా విస్తరించండి"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"స్క్రీన్షాట్"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"మళ్లీ చూపవద్దు"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"అన్నీ క్లియర్ చేయండి"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"నిర్వహించండి"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"నిశ్శబ్ద నోటిఫికేషన్లు"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"అన్ని నిశ్శబ్ద నోటిఫికేషన్లను క్లియర్ చేస్తుంది"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"అంతరాయం కలిగించవద్దు ద్వారా నోటిఫికేషన్లు పాజ్ చేయబడ్డాయి"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"ఇప్పుడే ప్రారంభించు"</string>
<string name="empty_shade_text" msgid="708135716272867002">"నోటిఫికేషన్లు లేవు"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"బ్లాక్ చేయి"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"చూపిస్తూనే ఉండు"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"కుదించు"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"నిశ్శబ్దం"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"నిశబ్దంగా తెలియజేయి"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"హెచ్చరించడం"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"ఎప్పటికప్పుడు హెచ్చరించు"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"నోటిఫికేషన్లను ఆఫ్ చేయి"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"ఈ యాప్ నుండి నోటిఫికేషన్లను చూపిస్తూ ఉండాలా?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"సాధారణ"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"ప్రాధాన్యం గలవి"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"కిందకు-లాగే షేడ్లో కనిపించే నోటిఫికేషన్ల మాత్రమే ద్వారా దృష్టి కేంద్రీకరించడానికి మీకు సహాయపడుతుంది. ఎల్లప్పుడూ నిశబ్దంగా ఉంచు."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"ప్రాధాన్యత గల నోటిఫికేషన్ల దిగువన ప్రదర్శిస్తుంది. ఎల్లప్పుడూ నిశబ్దంగా ఉంచు."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"ప్రాధాన్యత గల నోటిఫికేషన్ల దిగువన ప్రదర్శిస్తుంది. ఎల్లప్పుడూ నిశబ్దంగా ఉంచు."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"ప్రాధాన్యత గల నోటిఫికేషన్ల దిగువన ప్రదర్శిస్తుంది. ఎల్లప్పుడూ నిశబ్దంగా ఉంచు."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"శబ్దం & స్టేటస్ బార్ చిహ్నం ద్వారా మీరు దృష్టి సారించేలా చేస్తుంది. లాక్ స్క్రీన్పై చూపుతుంది."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"నిశ్శబ్దం"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"హెచ్చరించడం"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"శబ్దం లేదా వైబ్రేషన్ లేకుండా దృష్టి కేంద్రీకరించడానికి మీకు సహాయపడుతుంది."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"శబ్దం లేదా వైబ్రేషన్తో మీరు దృష్టి సారించేలా చేస్తుంది."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"ఈ నోటిఫికేషన్లను సవరించడం వీలుపడదు."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"ఈ నోటిఫికేషన్ల సమూహాన్ని ఇక్కడ కాన్ఫిగర్ చేయలేము"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"ప్రాక్సీ చేయబడిన నోటిఫికేషన్"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"అనుమతించు"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"తిరస్కరించు"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"బ్యాటరీ సేవర్ని షెడ్యూల్ చేయడానికి నొక్కండి"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"బ్యాటరీ ఛార్జింగ్ పూర్తిగా అయిపోతున్న తరుణంలో ఆన్ చేస్తుంది"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"వద్దు, ధన్యవాదాలు"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"బ్యాటరీ సేవర్ షెడ్యూల్ ఆన్ చేయబడింది"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"బ్యాటరీ <xliff:g id="PERCENTAGE">%d</xliff:g>%% కంటే తగ్గినప్పుడు బ్యాటరీ సేవర్ ఆటోమేటిక్గా ఆన్ అవుతుంది."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"దిగువ ఎడమవైపునకు తరలించు"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"దిగవు కుడివైపునకు జరుపు"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"విస్మరించు"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 465a2b8..f377c34 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"ไม่อนุญาตให้แก้ไขข้อบกพร่องผ่าน USB"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ผู้ใช้ที่ลงชื่อเข้าใช้อุปกรณ์อยู่ในขณะนี้ไม่สามารถเปิดการแก้ไขข้อบกพร่องผ่าน USB ได้ หากต้องการใช้ฟีเจอร์นี้ ให้เปลี่ยนไปเป็นผู้ใช้หลัก"</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"พอร์ต USB ถูกปิดใช้"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"พอร์ต USB ปิดใช้อยู่และจะไม่ตรวจหาอุปกรณ์เสริมใดๆ เพื่อปกป้องอุปกรณ์จากของเหลวและฝุ่นละออง \n\nคุณจะได้รับแจ้งเมื่อใช้พอร์ต USB ได้อีกครั้ง"</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"เปิดใช้พอร์ต USB แล้วเพื่อตรวจหาที่ชาร์จและอุปกรณ์เสริม"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"เปิดใช้ USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"ดูข้อมูลเพิ่มเติม"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"ขยายจนเต็มหน้าจอ"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"ยืดจนเต็มหน้าจอ"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"ภาพหน้าจอ"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"ไม่ต้องแสดงข้อความนี้อีก"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"ล้างทั้งหมด"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"จัดการ"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"การแจ้งเตือนแบบไม่มีเสียง"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"ล้างการแจ้งเตือนแบบไม่มีเสียงทั้งหมด"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"หยุดการแจ้งเตือนชั่วคราวโดย \"ห้ามรบกวน\""</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"เริ่มเลย"</string>
<string name="empty_shade_text" msgid="708135716272867002">"ไม่มีการแจ้งเตือน"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"บล็อก"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"แสดงต่อไป"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"ย่อเล็กสุด"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"ปิดเสียง"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"ปิดเสียงไว้"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"แจ้งเตือน"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"แจ้งเตือนต่อไป"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"ปิดการแจ้งเตือน"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"แสดงการแจ้งเตือนจากแอปนี้ต่อไปไหม"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"แจ้งเตือนเบาๆ"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"สำคัญ"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"ช่วยให้คุณโฟกัสกับการแจ้งเตือนในหน้าต่างแบบเลื่อนลงเท่านั้น ปิดเสียงตลอดเวลา"</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"แสดงใต้การแจ้งเตือนเรื่องสำคัญ ปิดเสียงตลอดเวลา"</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"แสดงใต้การแจ้งเตือนเรื่องสำคัญ ปิดเสียงตลอดเวลา"</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"แสดงใต้การแจ้งเตือนเรื่องสำคัญ ปิดเสียงตลอดเวลา"</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"ดึงความสนใจของคุณด้วยเสียงและไอคอนในแถบสถานะ แสดงในหน้าจอล็อก"</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"เงียบ"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"แจ้งเตือน"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"ช่วยรักษาสมาธิของคุณด้วยการไม่ส่งเสียงหรือสั่น"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"ดึงความสนใจของคุณด้วยเสียงและการสั่น"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"แก้ไขการแจ้งเตือนเหล่านี้ไม่ได้"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"การแจ้งเตือนกลุ่มนี้กำหนดค่าที่นี่ไม่ได้"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"การแจ้งเตือนที่ผ่านพร็อกซี"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"อนุญาต"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"ปฏิเสธ"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"แตะเพื่อตั้งเวลาโหมดประหยัดแบตเตอรี่"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"เปิดเมื่อมีแนวโน้มว่าแบตเตอรี่จะหมด"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"ไม่เป็นไร"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"เปิดใช้กำหนดเวลาโหมดประหยัดแบตเตอรี่อยู่"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"โหมดประหยัดแบตเตอรี่จะเปิดโดยอัตโนมัติเมื่อแบตเตอรี่เหลือไม่ถึง <xliff:g id="PERCENTAGE">%d</xliff:g>%%"</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"ย้ายไปด้านซ้ายล่าง"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"ย้ายไปด้านขาวล่าง"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"ปิด"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 8a832b0..90c7ab7 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Hindi pinapayagan ang pagde-debug sa pamamagitan ng USB"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Hindi mao-on ng user na kasalukuyang naka-sign in sa device na ito ang pag-debug ng USB. Upang magamit ang feature na ito, lumipat sa pangunahing user."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Na-disable ang USB port"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Para protektahan ang iyong device sa likido o dumi, na-disable ang USB port at hindi ito makaka-detect ng anumang accessory.\n\nAabisuhan ka kapag ayos nang gamitin ulit ang USB port."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Na-enable ang USB port para ma-detect ang mga charger at accessory"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"I-enable ang USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Matuto pa"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"I-zoom upang punan screen"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"I-stretch upang mapuno screen"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Huwag ipakitang muli"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"I-clear lahat"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Pamahalaan"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Mga silent na notification"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"I-clear ang lahat ng silent na notification"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Mga notification na na-pause ng Huwag Istorbohin"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Magsimula ngayon"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Walang mga notification"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"I-block"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Patuloy na ipakita"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"I-minimize"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Silent"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Manatiling naka-silent"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Mag-alerto"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Patuloy na mag-alerto"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"I-off ang mga notification"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Patuloy na ipakita ang mga notification mula sa app na ito?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Malumanay"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Priyoridad"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Nakakatulong para hindi ka maabala dahil sa pull-down na shade lang lalabas ang mga notification. Palaging naka-silent."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Ipinapakita sa ibaba ng mga priyoridad na notification. Palaging naka-silent."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Ipinapakita sa ibaba ng mga priyoridad na notification. Palaging naka-silent."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Ipinapakita sa ibaba ng mga priyoridad na notification. Palaging naka-silent."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Kinukuha ang iyong atensyon gamit ang tunog at icon sa status bar. Ipinapakita sa lock screen."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Naka-silent"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Mag-alerto"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Nakakatulong sa iyong tumuon nang walang tunog o pag-vibrate."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Kinukuha ang iyong atensyon sa pamamagitan ng tunog o pag-vibrate."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Hindi puwedeng baguhin ang mga notification na ito."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Hindi mako-configure dito ang pangkat na ito ng mga notification"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Na-proxy na notification"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Payagan"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Tanggihan"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"I-tap para iiskedyul ang Pangtipid sa Baterya"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"I-on kapag malamang na maubos ang baterya"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Hindi, salamat na lang"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Na-on ang iskedyul ng Pangtipid sa Baterya"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Awtomatikong mao-on ang Pangtipid sa Baterya kapag mas mababa na sa <xliff:g id="PERCENTAGE">%d</xliff:g>%% ang baterya."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Ilipat sa kaliwa sa ibaba"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Ilipat sa kanan sa ibaba"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"I-dismiss"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index c91b551..4432b41 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB hata ayıklama işlevine izin verilmiyor"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Bu cihazda geçerli olarak oturum açmış olan kullanıcı, USB hata ayıklama özelliğini açamaz. Bu özelliği kullanmak için birincil kullanıcıya geçin."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB bağlantı noktası devre dışı bırakıldı"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Cihazınızı sıvılardan veya tozlardan korumak için USB bağlantı noktası devre dışı bırakıldı ve aksesuarları algılamayacak.\n\nUSB bağlantı noktasını tekrar sorunsuz kullanabileceğiniz zaman bilgilendirileceksiniz."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB bağlantı noktası, şarj cihazlarını ve aksesuarları algılamak üzere etkinleştirildi"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB\'yi etkinleştir"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Daha fazla bilgi"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Yakınlaştır (ekranı kaplasın)"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Genişlet (ekran kapansın)"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Ekran görüntüsü"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Bir daha gösterme"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Tümünü temizle"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Yönet"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Sessiz bildirimler"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Sessiz bildirimlerin tümünü temizle"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Bildirimler, Rahatsız Etmeyin özelliği tarafından duraklatıldı"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Şimdi başlat"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Bildirim yok"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Engelle"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Göstermeye devam et"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Küçült"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Sessiz"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Sessiz uyarı göster"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Uyarı"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Uyarıda bulunmaya devam et"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Bildirimleri kapat"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Bu uygulamadan gelen bildirimler gösterilmeye devam edilsin mi?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Sessiz"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Öncelikli"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Bildirimleri yalnızca aşağıya açılır gölgede göstererek işinize odaklanmanızı sağlar. Her zaman sessizdir."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Öncelikli bildirimlerin altında görüntülenir. Her zaman sessizdir."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Öncelikli bildirimlerin altında görüntülenir. Her zaman sessizdir."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Öncelikli bildirimlerin altında görüntülenir. Her zaman sessizdir."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Ses ve bir durum çubuğu simgesiyle dikkatinizi çeker. Kilit ekranında gösterilir."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Sessiz"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Uyarı"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Ses veya titreşim olmadan odaklanmanıza yardımcı olur."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Ses veya titreşimle dikkatinizi çeker."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Bu bildirimler değiştirilemez."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Bu bildirim grubu burada yapılandırılamaz"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Proxy uygulanan bildirim"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"İzin ver"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Reddet"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Pil Tasarrufunu programlamak için dokunun"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Piliniz bitecek gibiyse bu özelliği açın"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Hayır, teşekkürler"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Pil Tasarruf programı açık"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Pil %%<xliff:g id="PERCENTAGE">%d</xliff:g> düzeyinin altına düştüğünde Pil Tasarrufu otomatik olarak açılacaktır."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Sol alta taşı"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Sağ alta taşı"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Kapat"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 7af05cd..171949a 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Ви не можете вмикати налагодження USB"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Користувач поточного облікового запису не може вмикати налагодження USB. Щоб увімкнути цю функцію, увійдіть в обліковий запис основного користувача."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB-порт вимкнено"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Щоб захистити ваш пристрій від рідини та сміття, USB-порт вимкнено. Він не виявлятиме жодних аксесуарів.\n\nКоли USB-порт можна буде використовувати, ви отримаєте сповіщення."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Порт USB виявлятиме зарядні пристрої та аксесуари"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Увімкнути USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Докладніше"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Масштабув. на весь екран"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Розтягнути на весь екран"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Знімок екрана"</string>
@@ -464,10 +462,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Більше не показувати"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Очистити все"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Керувати"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Беззвучні сповіщення"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Очистити всі беззвучні сповіщення"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Режим \"Не турбувати\" призупинив сповіщення"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Почати зараз"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Сповіщень немає"</string>
@@ -651,21 +647,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Блокувати"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Показувати надалі"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Згорнути"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Без звуку"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Без звуку"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Сповіщення"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Отримувати сповіщення"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Вимкнути сповіщення"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Чи показувати сповіщення з цього додатка надалі?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Тихі сповіщення"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Пріоритетні сповіщення"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Сповіщення показуються лише на розкривній панелі, щоб не відволікати. Завжди без звуку."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Показуються під пріоритетними сповіщеннями. Завжди без звуку."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Показуються під пріоритетними сповіщеннями. Завжди без звуку."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Показуються під пріоритетними сповіщеннями. Завжди без звуку."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Привертають увагу сигналом і значком у рядку стану. Показуються на заблокованому екрані."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Без звуку"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Зі звуком"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Не відволікає увагу звуковим сигналом або вібрацією."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Привертає увагу звуковим сигналом або вібрацією."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Ці сповіщення не можна змінити."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Цю групу сповіщень не можна налаштувати тут"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Проксі-сповіщення"</string>
@@ -918,8 +909,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Дозволити"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Відмовити"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Торкніться, щоб увімкнути автоматичний режим економії заряду акумулятора"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Вмикати, коли заряд акумулятора закінчується"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Ні, дякую"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Автоматичний режим економії заряду акумулятора ввімкнено"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Режим економії заряду акумулятора вмикається автоматично, коли рівень заряду нижчий за <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -952,4 +942,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Перемістити ліворуч униз"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Перемістити праворуч униз"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Закрити"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 05f90f9..7f37dea 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB ڈیبگ کرنے کی اجازت نہیں ہے"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"اس آلہ پر فی الحال سائن ان کردہ صارف USB ڈیبگنگ آن نہیں کر سکتا۔ اس خصوصیت کا استعمال کرنے کیلئے، ابتدائی صارف پر سوئچ کریں۔"</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB پورٹ غیر فعال ہو گیا"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"آپ کے آلے کی سیال یا دھول سے حفاظت کرنے کے لیے، USB پورٹ کو غیر فعال کر دیا گیا ہے اور یہ کسی لوازم کا پتہ نہیں لگا پائے گا۔\n\nUSB پورٹ کا دوبارہ استعمال کرنا ٹھیک ہونے پر آپ کو مطلع کیا جائے گا۔"</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"چارجرز اور لوازمات کا پتا لگانے کے لیے USB پورٹ فعال ہے"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB پورٹ فعال کریں"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"مزید جانیں"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"پوری سکرین پر زوم کریں"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"پوری سکرین پر پھیلائیں"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"اسکرین شاٹ"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"دوبارہ نہ دکھائیں"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"سبھی کو صاف کریں"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"نظم کریں"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"اطلاعات خاموش کریں"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"سبھی خاموش اطلاعات کو صاف کریں"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"\'ڈسٹرب نہ کریں\' کے ذریعے اطلاعات کو موقوف کیا گیا"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"ابھی شروع کریں"</string>
<string name="empty_shade_text" msgid="708135716272867002">"کوئی اطلاعات نہیں ہیں"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"مسدود کریں"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"دکھانا جاری رکھیں"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"چھوٹا کریں"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"خاموش کریں"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"خاموش رہیں"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"الرٹ کرنا"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"متنبہ کرنا جاری رکھیں"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"اطلاعات کو آف کریں"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"اس ایپ کی طرف سے اطلاعات دکھانا جاری رکھیں؟"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"لطیف"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"ترجیحی"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"صرف نیچے کی طرف کھینچیں شیڈ میں اطلاعات کے ساتھ فوکس کرنے میں آپ کی مدد کرتا ہے۔ ہمیشہ خاموش۔"</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"کم ترجیحی اطلاعات ڈسپلے کرتا ہے۔ ہمیشہ خاموش۔"</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"کم ترجیحی اطلاعات ڈسپلے کرتا ہے۔ ہمیشہ خاموش۔"</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"کم ترجیحی اطلاعات ڈسپلے کرتا ہے۔ ہمیشہ خاموش۔"</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"آواز اور اسٹیٹس بار کے آئیکن کے ساتھ آپ کی توجہ حاصل کرتا ہے۔ مقفل اسکرین پر دکھاتا ہے۔"</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"خاموش"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"الرٹ کرنا"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"بغیر آواز یا وائبریشن کے آپ کو فوکس کرنے میں مدد کرتا ہے۔"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"آواز اور وائبریشن کے ذریعے آپ کی توجہ حاصل کرتا ہے۔"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"ان اطلاعات کی ترمیم نہیں کی جا سکتی۔"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"اطلاعات کے اس گروپ کو یہاں کنفیگر نہیں کیا جا سکتا"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"پراکسی اطلاع"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"اجازت دیں"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"مسترد کریں"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"بیٹری سیور شیڈول کرنے کے لیے تھپتھپائیں"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"جب بیٹری کے ختم ہونے کا امکان ہو تو آن کریں"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"نہیں شکریہ"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"بیٹری سیور شیڈول آن ہو گیا"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"بیٹری کے <xliff:g id="PERCENTAGE">%d</xliff:g>%% سے کم ہونے پر بیٹری سیور خودکار طور پر آن ہو جائے گا۔"</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"نیچے بائیں جانب لے جائیں"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"نیچے دائیں جانب لے جائیں"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"برخاست کریں"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 2161527..2dfbb61 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USB orqali nosozliklarni tuzatishga ruxsat berilmagan"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Ayni paytda ushbu qurilmaga o‘z hisobi bilan kirgan foydalanuvchi USB orqali nosozliklarni tuzatish funksiyasini yoqa olmaydi. Bu funksiyadan foydalanish uchun asosiy foydalanuvchi profiliga o‘ting."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB port faolsizlashtirildi"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Qurilmangizni suyuqlik va turli parchalardan himoya qilish uchun USB port faolsizlashtiriladi va hech qanday aksessuarni aniqlay olmaydi.\n\nUSB portdan xavfsiz foydalanish mumkin boʻlganda, sizga xabar beriladi."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Quvvatlash moslamalari va aksessuarlarni aniqlash uchun USB port yoqildi"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"USB xususiyatini yoqish"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Batafsil"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"Ekranga moslashtirish"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Ekran hajmida cho‘zish"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Skrinshot"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Boshqa ko‘rsatilmasin"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Hammasini tozalash"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Boshqarish"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Ovozsiz bildirishnomalar"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Barcha tovushsiz bildirishnomalarni tozalash"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Bezovta qilinmasin rejimida bildirishnomalar pauza qilingan"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Boshlash"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Bildirishnomalar yo‘q"</string>
@@ -590,7 +586,7 @@
<string name="show_battery_percentage_summary" msgid="3215025775576786037">"Quvvat olmayotgan vaqtda batareya foizi holat qatorida chiqib turadi"</string>
<string name="quick_settings" msgid="10042998191725428">"Tezkor sozlamalar"</string>
<string name="status_bar" msgid="4877645476959324760">"Holat qatori"</string>
- <string name="overview" msgid="4018602013895926956">"Umumiy ma’lumot"</string>
+ <string name="overview" msgid="4018602013895926956">"Umumiy"</string>
<string name="demo_mode" msgid="2532177350215638026">"Tizim interfeysi demo rejimi"</string>
<string name="enable_demo_mode" msgid="4844205668718636518">"Demo rejimni yoqish"</string>
<string name="show_demo_mode" msgid="2018336697782464029">"Demo rejimni ko‘rsatish"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Bloklash"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Ha"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Kichraytirish"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Tovushsiz"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Ovozsiz qolsin"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Bildirishnoma yuborish"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Signal berishda davom etilsin"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Bildirishnoma kelmasin"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Bu ilovadan keladigan bildirishnomalar chiqaversinmi?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Tovushsiz"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Muhim"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Bildirishnomalar faqat maxsus panelda chiqadi. Doim tovushsiz."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Muhimlik darajasi past bildirishnomalarning chiqishi. Doim tovushsiz."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Muhimlik darajasi past bildirishnomalarning chiqishi. Doim tovushsiz."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Muhimlik darajasi past bildirishnomalarning chiqishi. Doim tovushsiz."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Kelgan bildirishnomalarni maxsus tovush va holat paneliga chiqadigan nishoncha orqali bilish mumkin. Bildirishnomalar ekran qulfida ham chiqadi."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Tovushsiz"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Bildirishnoma yuborish"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Bildirishnomalar tovush va tebranishsiz keladi."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Bildirishnomalar tovush va tebranish bilan keladi."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Bu bildirishnomalarni tahrirlash imkonsiz."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Ushbu bildirishnomalar guruhi bu yerda sozlanmaydi"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Ishonchli bildirishnoma"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Ruxsat"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Rad etish"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Quvvat tejash rejimini rejalashtirish uchun bosing"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Batareya quvvati kamayishi aniqlanganda yoqilsin"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Kerak emas"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Quvvat tejash rejimi jadvali faollashtirildi"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Batareya quvvati <xliff:g id="PERCENTAGE">%d</xliff:g>%% ga tushsa, quvvat tejash rejimi avtomatik ravishda yoqiladi."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Quyi chapga surish"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Quyi oʻngga surish"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Yopish"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index ace5215..659cd69 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Tính năng gỡ lỗi USB không được phép"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Người dùng hiện đã đăng nhập vào thiết bị này không thể bật tính năng gỡ lỗi USB. Để sử dụng tính năng này, hãy chuyển sang người dùng chính."</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"Đã tắt cổng USB"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"Để bảo vệ thiết bị của bạn khỏi chất lỏng hay mảnh vỡ, cổng USB sẽ tắt và không phát hiện được bất kỳ phụ kiện nào.\n\nBạn sẽ nhận được thông báo khi có thể sử dụng lại cổng USB."</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"Đã bật cổng USB để phát hiện bộ sạc và phụ kiện"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"Bật USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"Tìm hiểu thêm"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"T.phóng để lấp đầy m.hình"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"Giãn ra để lấp đầy m.hình"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Chụp màn hình"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"Không hiển thị lại"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"Xóa tất cả"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"Quản lý"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"Thông báo im lặng"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"Xóa tất cả thông báo im lặng"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"Chế độ Không làm phiền đã tạm dừng thông báo"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"Bắt đầu ngay"</string>
<string name="empty_shade_text" msgid="708135716272867002">"Không có thông báo nào"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"Chặn"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"Tiếp tục hiển thị"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"Thu nhỏ"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"Im lặng"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"Tiếp tục chế độ im lặng"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"Cảnh báo"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Tiếp tục cảnh báo"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Tắt thông báo"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Tiếp tục hiển thị các thông báo từ ứng dụng này?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Nhẹ nhàng"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Ưu tiên"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Giúp bạn chỉ tập trung vào thông báo trong danh sách kéo xuống. Luôn im lặng."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Hiển thị bên dưới thông báo mức độ ưu tiên. Luôn im lặng."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Hiển thị bên dưới thông báo mức độ ưu tiên. Luôn im lặng."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Hiển thị bên dưới thông báo mức độ ưu tiên. Luôn im lặng."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Thu hút sự chú ý của bạn bằng biểu tượng thanh trạng thái và âm thanh. Hiển thị trên màn hình khóa."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Im lặng"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Cảnh báo"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Giúp bạn tập trung bằng cách tắt tiếng hoặc không rung."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Thu hút sự chú ý của bạn bằng cách bật tiếng hoặc rung."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Không thể sửa đổi các thông báo này."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Không thể định cấu hình nhóm thông báo này tại đây"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Thông báo đã xử lý qua máy chủ proxy"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"Cho phép"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Từ chối"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"Nhấn để lên lịch Trình tiết kiệm pin"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"Bật khi pin sắp hết"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"Không, cảm ơn"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"Đã bật Trình tiết kiệm pin được lên lịch"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"Trình tiết kiệm pin sẽ tự động bật khi mức pin thấp hơn <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Chuyển tới dưới cùng bên trái"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Chuyển tới dưới cùng bên phải"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Loại bỏ"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 8675e31..018777f 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"不允许使用 USB 调试功能"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"目前已登录此设备的用户无法开启 USB 调试功能。要使用此功能,请切换为主要用户的帐号。"</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB 端口已停用"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"为避免液体或碎屑导致您的设备受损,系统已停用 USB 端口,因此目前无法检测任何配件。\n\n系统会在重新允许使用 USB 端口时通知您。"</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB 端口已启用,可检测充电器和配件"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"启用 USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"了解详情"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"缩放以填满屏幕"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"拉伸以填满屏幕"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"屏幕截图"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"不再显示"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"全部清除"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"管理"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"无声通知"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"清除所有无声通知"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"勿扰模式暂停的通知"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"立即开始"</string>
<string name="empty_shade_text" msgid="708135716272867002">"没有通知"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"屏蔽"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"继续显示"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"最小化"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"静音"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"显示通知但不发出提示音"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"提醒"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"继续提醒"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"关闭通知"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"要继续显示来自此应用的通知吗?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"无声通知"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"已设为优先"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"只会在下拉通知栏中显示通知,引起您的注意。一律不发出提示音。"</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"显示在优先通知下方。一律不发出提示音。"</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"显示在优先通知下方。一律不发出提示音。"</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"显示在优先通知下方。一律不发出提示音。"</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"通过提示音和状态栏图标吸引您的注意。这类通知会显示在锁定屏幕上。"</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"静音"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"提醒"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"不会发出提示音或振动,可帮助您保持专注。"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"通过提示音或振动吸引您的注意。"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"无法修改这些通知。"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"您无法在此处配置这组通知"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"代理通知"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"允许"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"拒绝"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"点按即可预设省电模式"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"在电池电量可能会耗尽时,系统会开启此模式"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"不用了"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"预设的省电模式已开启"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"一旦电池电量降到 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 以下,省电模式就会自动开启。"</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"移至左下角"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"移至右下角"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"关闭"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 002e40c..cd674f5 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"不允許 USB 偵錯"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"目前登入此裝置的使用者無法啟用 USB 偵錯功能。如要使用此功能,請切換至主要使用者。"</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"已停用 USB 連接埠"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"為了保護您的裝置免受液體或碎片損害,USB 連接埠已停用,因此不會偵測到任何配件。\n\nUSB 連接埠可安全使用時,您會收到通知。"</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"已啟用 USB 連接埠以偵測充電器和配件"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"啟用 USB"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"瞭解詳情"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"放大為全螢幕"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"放大為全螢幕"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"螢幕截圖"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"不用再顯示"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"全部清除"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"管理"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"靜音通知"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"清除所有靜音通知"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"「請勿騷擾」模式已將通知暫停"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"立即開始"</string>
<string name="empty_shade_text" msgid="708135716272867002">"沒有通知"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"封鎖"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"繼續顯示"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"最小化"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"靜音"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"保持靜音"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"發出提醒"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"繼續提示"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"關閉通知"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"要繼續顯示此應用程式的通知嗎?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"低重要性"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"優先"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"協助您只查看下拉式通知欄中的通知。一律靜音。"</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"顯示不重要的通知。一律靜音。"</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"顯示不重要的通知。一律靜音。"</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"顯示不重要的通知。一律靜音。"</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"透過聲音和狀態列圖示引起您的注意,並在上鎖畫面顯示。"</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"靜音"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"發出提醒"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"助您保持專注,不會發出聲音或震動。"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"發出聲音或震動來吸引您的注意。"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"無法修改這些通知。"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"無法在此設定這組通知"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"代理通知"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"允許"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"拒絕"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"輕按即可預定省電模式自動開啟時間"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"在電池電量可能耗盡前啟用「省電模式」"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"不用了,謝謝"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"已預定省電模式開啟時間"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"省電模式將會在電量低於 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 時自動開啟。"</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"移去左下角"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"移去右下角"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"關閉"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 3824e23..f16b21c 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -62,12 +62,10 @@
<string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"無權使用 USB 偵錯功能"</string>
<string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"目前登入這個裝置的使用者無法啟用 USB 偵錯功能。如要使用這項功能,請切換到主要使用者。"</string>
<string name="usb_contaminant_title" msgid="206854874263058490">"USB 連接埠已停用"</string>
- <!-- no translation found for usb_contaminant_message (7379089091591609111) -->
- <skip />
+ <string name="usb_contaminant_message" msgid="7379089091591609111">"為了避免液體或灰塵導致你的裝置受損,系統已停用 USB 連接埠,因此目前無法偵測任何配件。\n\n系統會在可繼續使用 USB 連接埠時通知你。"</string>
<string name="usb_port_enabled" msgid="7906141351687694867">"USB 通訊埠已啟用,可偵測充電器和配件"</string>
<string name="usb_disable_contaminant_detection" msgid="2103905315747120033">"啟用 USB 連接埠"</string>
- <!-- no translation found for learn_more (5000517160980853569) -->
- <skip />
+ <string name="learn_more" msgid="5000517160980853569">"瞭解詳情"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"放大為全螢幕"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"放大為全螢幕"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"擷取螢幕畫面"</string>
@@ -458,10 +456,8 @@
<string name="media_projection_remember_text" msgid="3103510882172746752">"不要再顯示"</string>
<string name="clear_all_notifications_text" msgid="814192889771462828">"全部清除"</string>
<string name="manage_notifications_text" msgid="2386728145475108753">"管理"</string>
- <!-- no translation found for notification_section_header_gentle (4372438504154095677) -->
- <skip />
- <!-- no translation found for accessibility_notification_section_header_gentle_clear_all (4286716295850400959) -->
- <skip />
+ <string name="notification_section_header_gentle" msgid="4372438504154095677">"無聲通知"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="4286716295850400959">"清除所有無聲通知"</string>
<string name="dnd_suppressing_shade_text" msgid="1904574852846769301">"「零打擾」模式已將通知設為暫停"</string>
<string name="media_projection_action_text" msgid="8470872969457985954">"立即開始"</string>
<string name="empty_shade_text" msgid="708135716272867002">"沒有通知"</string>
@@ -645,21 +641,16 @@
<string name="inline_block_button" msgid="8735843688021655065">"封鎖"</string>
<string name="inline_keep_button" msgid="6665940297019018232">"繼續顯示"</string>
<string name="inline_minimize_button" msgid="966233327974702195">"最小化"</string>
- <!-- no translation found for inline_silent_button_silent (5315879183296940969) -->
- <skip />
+ <string name="inline_silent_button_silent" msgid="5315879183296940969">"無聲"</string>
<string name="inline_silent_button_stay_silent" msgid="6308371431217601009">"繼續顯示通知但不發出音效"</string>
- <!-- no translation found for inline_silent_button_alert (6008435419895088034) -->
- <skip />
+ <string name="inline_silent_button_alert" msgid="6008435419895088034">"快訊"</string>
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"繼續顯示通知"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"關閉通知"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"要繼續顯示這個應用程式的通知嗎?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"無聲通知"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"已設為優先"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"只會在下拉式通知欄中顯示通知,且一律不發出音效。"</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"顯示在優先通知下方,且一律不發出音效。"</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"顯示在優先通知下方,且一律不發出音效。"</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"顯示在優先通知下方,且一律不發出音效。"</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"發出音效並顯示「狀態列」圖示吸引你的注意力。這類通知會顯示在螢幕鎖定畫面上。"</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"靜音"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"快訊"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"協助你不受音效或震動干擾。"</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"發出音效或震動吸引你的注意力。"</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"無法修改這些通知。"</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"無法在這裡設定這個通知群組"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"經過 Proxy 處理的通知"</string>
@@ -726,7 +717,7 @@
<string name="keyboard_key_num_lock" msgid="5052537581246772117">"Num Lock 鍵"</string>
<string name="keyboard_key_numpad_template" msgid="8729216555174634026">"數字鍵 <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"系統"</string>
- <string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"主畫面"</string>
+ <string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"主螢幕"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"最近"</string>
<string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"返回"</string>
<string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"通知"</string>
@@ -908,8 +899,7 @@
<string name="slice_permission_allow" msgid="2340244901366722709">"允許"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"拒絕"</string>
<string name="auto_saver_title" msgid="1217959994732964228">"輕觸即可排定節約耗電量模式自動開啟的情況"</string>
- <!-- no translation found for auto_saver_text (2563289953551438248) -->
- <skip />
+ <string name="auto_saver_text" msgid="2563289953551438248">"在電池電量即將耗盡時開啟"</string>
<string name="no_auto_saver_action" msgid="8086002101711328500">"不用了,謝謝"</string>
<string name="auto_saver_enabled_title" msgid="6726474226058316862">"已按照排定開啟節約耗電量模式"</string>
<string name="auto_saver_enabled_text" msgid="874711029884777579">"節約耗電量模式會在電量低於 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 時自動開啟。"</string>
@@ -942,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"移至左下方"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"移至右下方"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"關閉"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 30b2e6e..887bec0 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -647,13 +647,10 @@
<string name="inline_silent_button_keep_alerting" msgid="327696842264359693">"Qhubeka wazise"</string>
<string name="inline_turn_off_notifications" msgid="8635596135532202355">"Vala izaziso"</string>
<string name="inline_keep_showing_app" msgid="1723113469580031041">"Qhubeka nokubonisa izaziso kusuka kulolu hlelo lokusebenza?"</string>
- <string name="notification_silence_title" msgid="7352089096356977930">"Okumnene"</string>
- <string name="notification_alert_title" msgid="3966526305405016221">"Okwenziwe kwabaluleka"</string>
- <string name="notification_channel_summary_low" msgid="1065819618107531284">"Ikusiza ukuthi ugxile ngezaziso kuphela kumthunzi wokudonsela phansi. Ihlala ithulile."</string>
- <string name="notification_channel_summary_low_status" msgid="2702170424808743755">"Ibonisa izaziso ezibalulekile ezingezansi. Ihlala ithulile."</string>
- <string name="notification_channel_summary_low_lock" msgid="7966605244472624458">"Ibonisa izaziso ezibalulekile ezingezansi. Ihlala ithulile."</string>
- <string name="notification_channel_summary_low_status_lock" msgid="7012562768950012739">"Ibonisa izaziso ezibalulekile ezingezansi. Ihlala ithulile."</string>
- <string name="notification_channel_summary_default" msgid="3847289783382316019">"Yenza uyinake ngomsindo nesithonjana sebha yesimo. Iboniswa kusikrini sokukhiya."</string>
+ <string name="notification_silence_title" msgid="5763240612242137433">"Kuthulile"</string>
+ <string name="notification_alert_title" msgid="8031196611815490340">"Iyazisa"</string>
+ <string name="notification_channel_summary_low" msgid="3387466082089715555">"Ikusiza ukuthi ugxile ngaphandle komsindo noma ukudlidliza."</string>
+ <string name="notification_channel_summary_default" msgid="5994062840431965586">"Ithola ukunaka kwakho ngomsindo noma ukudlidliza."</string>
<string name="notification_unblockable_desc" msgid="4556908766584964102">"Lezi zaziso azikwazi ukushintshwa."</string>
<string name="notification_multichannel_desc" msgid="4695920306092240550">"Leli qembu lezaziso alikwazi ukulungiselelwa lapha"</string>
<string name="notification_delegate_header" msgid="2857691673814814270">"Isaziso sommeli"</string>
@@ -935,4 +932,8 @@
<string name="bubble_accessibility_action_move_bottom_left" msgid="206369104473183217">"Hambisa inkinobho ngakwesokunxele"</string>
<string name="bubble_accessibility_action_move_bottom_right" msgid="8705660152384312329">"Hambisa inkinobho ngakwesokudla"</string>
<string name="bubble_dismiss_text" msgid="8028337712674081668">"Cashisa"</string>
+ <!-- no translation found for notification_content_system_nav_changed (7218093915747788444) -->
+ <skip />
+ <!-- no translation found for notification_content_gesture_nav_available (8111130443656460792) -->
+ <skip />
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8174434..3fe2492 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -495,6 +495,9 @@
<!-- Content description of the battery level icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_battery_level">Battery <xliff:g id="number">%d</xliff:g> percent.</string>
+ <!-- Content description of the battery level icon for accessibility, including the estimated time remaining before the phone runs out of battery (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_battery_level_with_estimate">Battery <xliff:g id="percentage" example="95%">%1$s</xliff:g> percent, about <xliff:g id="time" example="Until 3:15pm">%2$s</xliff:g> left based on your usage</string>
+
<!-- Content description of the battery level icon for accessibility while the device is charging (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_battery_level_charging">Battery charging, <xliff:g id="battery_percentage">%d</xliff:g> percent.</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index cc3a67c..240691b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -122,13 +122,21 @@
* disabled.
*/
public static boolean isAssistantGestureDisabled(int sysuiStateFlags) {
- // Disable when in screen pinning, immersive, the bouncer is showing, or the notifications
- // are interactive
+ // Disable when in screen pinning, immersive, the bouncer is showing
int disableFlags = SYSUI_STATE_SCREEN_PINNING
| SYSUI_STATE_NAV_BAR_HIDDEN
- | SYSUI_STATE_BOUNCER_SHOWING
- | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
- return (sysuiStateFlags & disableFlags) != 0;
+ | SYSUI_STATE_BOUNCER_SHOWING;
+ if ((sysuiStateFlags & disableFlags) != 0) {
+ return true;
+ }
+
+ // Disable when notifications are showing (only if unlocked)
+ if ((sysuiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0
+ && (sysuiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING) == 0) {
+ return true;
+ }
+
+ return false;
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 873874f..26d3d86 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -111,6 +111,7 @@
private static final String TAG = "KeyguardUpdateMonitor";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES;
+ private static final boolean DEBUG_FACE = true;
private static final int LOW_BATTERY_THRESHOLD = 20;
private static final String ACTION_FACE_UNLOCK_STARTED
@@ -182,6 +183,7 @@
* Prudently disable lockscreen.
*/
public static final boolean CORE_APPS_ONLY;
+
static {
try {
CORE_APPS_ONLY = IPackageManager.Stub.asInterface(
@@ -734,6 +736,7 @@
if (acquireInfo != FaceManager.FACE_ACQUIRED_GOOD) {
return;
}
+ if (DEBUG_FACE) Log.d(TAG, "Face acquired");
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -760,6 +763,7 @@
Log.d(TAG, "Face authentication disabled by DPM for userId: " + userId);
return;
}
+ if (DEBUG_FACE) Log.d(TAG, "Face auth succeeded for user " + userId);
onFaceAuthenticated(userId);
} finally {
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
@@ -768,6 +772,7 @@
}
private void handleFaceHelp(int msgId, String helpString) {
+ if (DEBUG_FACE) Log.d(TAG, "Face help received: " + helpString);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -786,6 +791,7 @@
};
private void handleFaceError(int msgId, String errString) {
+ if (DEBUG_FACE) Log.d(TAG, "Face error received: " + errString);
if (msgId == FaceManager.FACE_ERROR_CANCELED
&& mFaceRunningState == BIOMETRIC_STATE_CANCELLING_RESTARTING) {
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 9f4c403e..9edb54c 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -15,6 +15,8 @@
*/
package com.android.keyguard.clock;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLOCK_FACE_BLACKLIST;
+
import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
@@ -24,9 +26,12 @@
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.view.LayoutInflater;
import androidx.annotation.VisibleForTesting;
@@ -42,10 +47,12 @@
import com.android.systemui.util.InjectionInflationController;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;
+import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -67,6 +74,8 @@
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
private final CurrentUserObservable mCurrentUserObservable;
+ private final ArraySet<String> mBlacklistedClockPlugins = new ArraySet<>();
+
/**
* Observe settings changes to know when to switch the clock face.
*/
@@ -155,6 +164,41 @@
DisplayMetrics dm = res.getDisplayMetrics();
mWidth = dm.widthPixels;
mHeight = dm.heightPixels;
+
+ updateBlackList();
+ registerDeviceConfigListener();
+ }
+
+ private void updateBlackList() {
+ String blacklist = getBlackListFromConfig();
+
+ mBlacklistedClockPlugins.clear();
+ if (blacklist != null && !blacklist.isEmpty()) {
+ mBlacklistedClockPlugins.addAll(Arrays.asList(blacklist.split(",")));
+ }
+ }
+
+ String getBlackListFromConfig() {
+ return DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_SYSTEMUI, CLOCK_FACE_BLACKLIST, null);
+ }
+
+ private void registerDeviceConfigListener() {
+ DeviceConfig.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ r -> mMainHandler.post(r),
+ properties -> onDeviceConfigPropertiesChanged(properties.getNamespace()));
+ }
+
+ void onDeviceConfigPropertiesChanged(String namespace) {
+ if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) {
+ Log.e(TAG, "Received update from DeviceConfig for unrelated namespace: "
+ + namespace);
+ return;
+ }
+
+ updateBlackList();
+ reload();
}
/**
@@ -240,9 +284,9 @@
}
private void reload() {
- mPreviewClocks.reload();
+ mPreviewClocks.reloadCurrentClock();
mListeners.forEach((listener, clocks) -> {
- clocks.reload();
+ clocks.reloadCurrentClock();
ClockPlugin clock = clocks.getCurrentClock();
if (clock instanceof DefaultClockController) {
listener.onClockChanged(null);
@@ -287,20 +331,13 @@
@Override
public void onPluginConnected(ClockPlugin plugin, Context pluginContext) {
addClockPlugin(plugin);
- reload();
- if (plugin == mCurrentClock) {
- ClockManager.this.reload();
- }
+ reloadIfNeeded(plugin);
}
@Override
public void onPluginDisconnected(ClockPlugin plugin) {
- boolean isCurrentClock = plugin == mCurrentClock;
removeClockPlugin(plugin);
- reload();
- if (isCurrentClock) {
- ClockManager.this.reload();
- }
+ reloadIfNeeded(plugin);
}
/**
@@ -313,10 +350,12 @@
}
/**
- * Get information about available clock faces.
+ * Get information about clock faces which are available and not in blacklist.
*/
List<ClockInfo> getInfo() {
- return mClockInfo;
+ return mClockInfo.stream()
+ .filter(info -> !mBlacklistedClockPlugins.contains(info.getId()))
+ .collect(Collectors.toList());
}
/**
@@ -347,10 +386,19 @@
}
}
+ private void reloadIfNeeded(ClockPlugin plugin) {
+ final boolean wasCurrentClock = plugin == mCurrentClock;
+ reloadCurrentClock();
+ final boolean isCurrentClock = plugin == mCurrentClock;
+ if (wasCurrentClock || isCurrentClock) {
+ ClockManager.this.reload();
+ }
+ }
+
/**
* Update the current clock.
*/
- void reload() {
+ void reloadCurrentClock() {
mCurrentClock = getClockPlugin();
}
@@ -359,7 +407,7 @@
if (ClockManager.this.isDocked()) {
final String name = mSettingsWrapper.getDockedClockFace(
mCurrentUserObservable.getCurrentUser().getValue());
- if (name != null) {
+ if (name != null && !mBlacklistedClockPlugins.contains(name)) {
plugin = mClocks.get(name);
if (plugin != null) {
return plugin;
@@ -368,7 +416,7 @@
}
final String name = mSettingsWrapper.getLockScreenCustomClockFace(
mCurrentUserObservable.getCurrentUser().getValue());
- if (name != null) {
+ if (name != null && !mBlacklistedClockPlugins.contains(name)) {
plugin = mClocks.get(name);
}
return plugin;
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index bce5c23..f66463e 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -356,8 +356,8 @@
if (estimate != null) {
mBatteryPercentView.setText(estimate);
setContentDescription(getContext().getString(
- R.string.battery_low_percent_format_hybrid, mLevel, estimate));
-
+ R.string.accessibility_battery_level_with_estimate,
+ mLevel, estimate));
} else {
setPercentTextAtCurrentLevel();
}
diff --git a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
index 528db5a..691c3f9 100644
--- a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
+++ b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java
@@ -36,9 +36,9 @@
public class CornerHandleView extends View {
private static final boolean ALLOW_TUNING = false;
private static final int ANGLE_DEGREES = 50;
- public static final int MARGIN_DP = 11;
- public static final int RADIUS_DP = 37;
- public static final float STROKE_DP = 2.5f;
+ private static final float STROKE_DP = 2.5f;
+ // Radius to use if none is available.
+ private static final int FALLBACK_RADIUS_DP = 15;
private Paint mPaint;
private int mLightColor;
@@ -71,7 +71,7 @@
/**
* Receives an intensity from 0 (lightest) to 1 (darkest) and sets the handle color
- * approriately. Intention is to match the home handle color.
+ * appropriately. Intention is to match the home handle color.
*/
public void updateDarkness(float darkIntensity) {
mPaint.setColor((int) ArgbEvaluator.getInstance().evaluate(darkIntensity,
@@ -115,20 +115,37 @@
}
private int getMarginPx() {
+ // Hand-derived function to map radiusPx to the margin amount.
+ // https://www.wolframalpha.com/input/?i=0.001402+*+x+%5E2%E2%88%920.08661+*+x%2B17.20+from+40+to+180
+ int radius = getRadiusPx();
+ int marginPx = (int) (0.001402f * radius * radius - 0.08661f * radius + 17.2f);
if (ALLOW_TUNING) {
- return SystemProperties.getInt("CORNER_HANDLE_MARGIN_PX",
- (int) convertDpToPixel(MARGIN_DP, getContext()));
+ return SystemProperties.getInt("CORNER_HANDLE_MARGIN_PX", marginPx);
} else {
- return (int) convertDpToPixel(MARGIN_DP, getContext());
+ return marginPx;
}
}
private int getRadiusPx() {
+ // Attempt to get the bottom corner radius, otherwise fall back on the generic or top
+ // values. If none are available, use the FALLBACK_RADIUS_DP.
+ int radius = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.rounded_corner_radius_bottom);
+ if (radius == 0) {
+ radius = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.rounded_corner_radius);
+ }
+ if (radius == 0) {
+ radius = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.rounded_corner_radius_top);
+ }
+ if (radius == 0) {
+ radius = (int) convertDpToPixel(FALLBACK_RADIUS_DP, mContext);
+ }
if (ALLOW_TUNING) {
- return SystemProperties.getInt("CORNER_HANDLE_RADIUS_PX",
- (int) convertDpToPixel(RADIUS_DP, getContext()));
+ return SystemProperties.getInt("CORNER_HANDLE_RADIUS_PX", radius);
} else {
- return (int) convertDpToPixel(RADIUS_DP, getContext());
+ return radius;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 7a82402..9f4a4e0 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -48,7 +48,6 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.PowerUI;
-import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -287,7 +286,6 @@
@Inject Lazy<SensorPrivacyManager> mSensorPrivacyManager;
@Inject Lazy<AutoHideController> mAutoHideController;
@Inject Lazy<ForegroundServiceNotificationListener> mForegroundServiceNotificationListener;
- @Inject Lazy<PrivacyItemController> mPrivacyItemController;
@Inject @Named(BG_LOOPER_NAME) Lazy<Looper> mBgLooper;
@Inject @Named(BG_HANDLER_NAME) Lazy<Handler> mBgHandler;
@Inject @Named(MAIN_HANDLER_NAME) Lazy<Handler> mMainHandler;
@@ -472,7 +470,6 @@
mProviders.put(ForegroundServiceNotificationListener.class,
mForegroundServiceNotificationListener::get);
mProviders.put(ClockManager.class, mClockManager::get);
- mProviders.put(PrivacyItemController.class, mPrivacyItemController::get);
mProviders.put(ActivityManagerWrapper.class, mActivityManagerWrapper::get);
mProviders.put(DevicePolicyManagerWrapper.class, mDevicePolicyManagerWrapper::get);
mProviders.put(PackageManagerWrapper.class, mPackageManagerWrapper::get);
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 4aaf85a..5e19219 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -199,6 +199,10 @@
return;
}
+ if (mOverlay == null || mBottomOverlay == null) {
+ return;
+ }
+
if (mAssistHintVisible != visible) {
mAssistHintVisible = visible;
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 3fc6689..afb8e74 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -20,7 +20,6 @@
import android.app.AppOpsManager;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
@@ -100,6 +99,13 @@
} else {
mAppOps.stopWatchingActive(this);
mAppOps.stopWatchingNoted(this);
+ mBGHandler.removeCallbacksAndMessages(null); // null removes all
+ synchronized (mActiveItems) {
+ mActiveItems.clear();
+ }
+ synchronized (mNotedItems) {
+ mNotedItems.clear();
+ }
}
}
@@ -204,59 +210,6 @@
}
/**
- * Does the app-op code refer to a user sensitive permission for the specified user id
- * and package. Only user sensitive permission should be shown to the user by default.
- *
- * @param appOpCode The code of the app-op.
- * @param uid The uid of the user.
- * @param packageName The name of the package.
- *
- * @return {@code true} iff the app-op item is user sensitive
- */
- private boolean isUserSensitive(int appOpCode, int uid, String packageName) {
- String permission = AppOpsManager.opToPermission(appOpCode);
- if (permission == null) {
- return false;
- }
- int permFlags = mContext.getPackageManager().getPermissionFlags(permission,
- packageName, UserHandle.getUserHandleForUid(uid));
- return (permFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
- }
-
- /**
- * Does the app-op item refer to an operation that should be shown to the user.
- * Only specficic ops (like SYSTEM_ALERT_WINDOW) or ops that refer to user sensitive
- * permission should be shown to the user by default.
- *
- * @param item The item
- *
- * @return {@code true} iff the app-op item should be shown to the user
- */
- private boolean isUserVisible(AppOpItem item) {
- return isUserVisible(item.getCode(), item.getUid(), item.getPackageName());
- }
-
-
- /**
- * Does the app-op, uid and package name, refer to an operation that should be shown to the
- * user. Only specficic ops (like {@link AppOpsManager.OP_SYSTEM_ALERT_WINDOW}) or
- * ops that refer to user sensitive permission should be shown to the user by default.
- *
- * @param item The item
- *
- * @return {@code true} iff the app-op for should be shown to the user
- */
- private boolean isUserVisible(int appOpCode, int uid, String packageName) {
- // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission
- // which may be user senstive, so for now always show it to the user.
- if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) {
- return true;
- }
-
- return isUserSensitive(appOpCode, uid, packageName);
- }
-
- /**
* Returns a copy of the list containing all the active AppOps that the controller tracks.
*
* @return List of active AppOps information
@@ -279,8 +232,8 @@
final int numActiveItems = mActiveItems.size();
for (int i = 0; i < numActiveItems; i++) {
AppOpItem item = mActiveItems.get(i);
- if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId)
- && isUserVisible(item)) {
+ if ((userId == UserHandle.USER_ALL
+ || UserHandle.getUserId(item.getUid()) == userId)) {
list.add(item);
}
}
@@ -289,8 +242,8 @@
final int numNotedItems = mNotedItems.size();
for (int i = 0; i < numNotedItems; i++) {
AppOpItem item = mNotedItems.get(i);
- if ((userId == UserHandle.USER_ALL || UserHandle.getUserId(item.getUid()) == userId)
- && isUserVisible(item)) {
+ if ((userId == UserHandle.USER_ALL
+ || UserHandle.getUserId(item.getUid()) == userId)) {
list.add(item);
}
}
@@ -316,8 +269,7 @@
}
private void notifySuscribers(int code, int uid, String packageName, boolean active) {
- if (mCallbacksByCode.containsKey(code)
- && isUserVisible(code, uid, packageName)) {
+ if (mCallbacksByCode.containsKey(code)) {
for (Callback cb: mCallbacksByCode.get(code)) {
cb.onActiveStateChanged(code, uid, packageName, active);
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
index 01deb03..6d109fb 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java
@@ -142,6 +142,10 @@
mHandler.post(() -> maybeShowHandles(/* ignoreThreshold = */ true));
}
+ boolean areHandlesShowing() {
+ return mHandlesShowing;
+ }
+
void onAssistantGesturePerformed() {
mBehaviorMap.get(mCurrentBehavior).onAssistantGesturePerformed();
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 2fc79d6..67fcd68 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -104,6 +104,11 @@
public static final int INVOCATION_TYPE_QUICK_SEARCH_BAR = 4;
public static final int INVOCATION_HOME_BUTTON_LONG_PRESS = 5;
+ public static final int DISMISS_REASON_INVOCATION_CANCELLED = 1;
+ public static final int DISMISS_REASON_TAP = 2;
+ public static final int DISMISS_REASON_BACK = 3;
+ public static final int DISMISS_REASON_TIMEOUT = 4;
+
private static final long TIMEOUT_SERVICE = 2500;
private static final long TIMEOUT_ACTIVITY = 1000;
@@ -251,13 +256,14 @@
if (invocationType == INVOCATION_TYPE_GESTURE) {
mHandleController.onAssistantGesturePerformed();
}
- args.putInt(INVOCATION_PHONE_STATE_KEY, mPhoneStateMonitor.getPhoneState());
+ int phoneState = mPhoneStateMonitor.getPhoneState();
+ args.putInt(INVOCATION_PHONE_STATE_KEY, phoneState);
args.putLong(INVOCATION_TIME_MS_KEY, SystemClock.uptimeMillis());
// Logs assistant start with invocation type.
MetricsLogger.action(
new LogMaker(MetricsEvent.ASSISTANT)
- .setType(MetricsEvent.TYPE_OPEN).setSubtype(
- invocationType));
+ .setType(MetricsEvent.TYPE_OPEN)
+ .setSubtype(toLoggingSubType(invocationType, phoneState)));
startAssistInternal(args, assistComponent, isService);
}
@@ -437,4 +443,19 @@
public void onLockscreenShown() {
mAssistUtils.onLockscreenShown();
}
+
+ /** Returns the logging flags for the given Assistant invocation type. */
+ public int toLoggingSubType(int invocationType) {
+ return toLoggingSubType(invocationType, mPhoneStateMonitor.getPhoneState());
+ }
+
+ private int toLoggingSubType(int invocationType, int phoneState) {
+ // Note that this logic will break if the number of Assistant invocation types exceeds 7.
+ // There are currently 5 invocation types, but we will be migrating to the new logging
+ // framework in the next update.
+ int subType = mHandleController.areHandlesShowing() ? 0 : 1;
+ subType |= invocationType << 1;
+ subType |= phoneState << 4;
+ return subType;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
index b1be811..95c136f 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
@@ -16,6 +16,8 @@
package com.android.systemui.assist.ui;
+import static com.android.systemui.assist.AssistManager.DISMISS_REASON_INVOCATION_CANCELLED;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -91,6 +93,8 @@
@Override // AssistManager.UiController
public void onInvocationProgress(int type, float progress) {
+ boolean invocationWasInProgress = mInvocationInProgress;
+
if (progress == 1) {
animateInvocationCompletion(type, 0);
} else if (progress == 0) {
@@ -105,16 +109,7 @@
}
mLastInvocationProgress = progress;
- // Logs assistant invocation start.
- if (!mInvocationInProgress && progress > 0.f) {
- MetricsLogger.action(new LogMaker(MetricsEvent.ASSISTANT)
- .setType(MetricsEvent.TYPE_ACTION));
- }
- // Logs assistant invocation cancelled.
- if (mInvocationInProgress && progress == 0f) {
- MetricsLogger.action(new LogMaker(MetricsEvent.ASSISTANT)
- .setType(MetricsEvent.TYPE_DISMISS).setSubtype(0));
- }
+ logInvocationProgressMetrics(type, progress, invocationWasInProgress);
}
@Override // AssistManager.UiController
@@ -142,6 +137,22 @@
mInvocationLightsView.setColors(color1, color2, color3, color4);
}
+ protected static void logInvocationProgressMetrics(
+ int type, float progress, boolean invocationWasInProgress) {
+ // Logs assistant invocation start.
+ if (!invocationWasInProgress && progress > 0.f) {
+ MetricsLogger.action(new LogMaker(MetricsEvent.ASSISTANT)
+ .setType(MetricsEvent.TYPE_ACTION)
+ .setSubtype(Dependency.get(AssistManager.class).toLoggingSubType(type)));
+ }
+ // Logs assistant invocation cancelled.
+ if (invocationWasInProgress && progress == 0f) {
+ MetricsLogger.action(new LogMaker(MetricsEvent.ASSISTANT)
+ .setType(MetricsEvent.TYPE_DISMISS)
+ .setSubtype(DISMISS_REASON_INVOCATION_CANCELLED));
+ }
+ }
+
private void updateAssistHandleVisibility() {
ScreenDecorations decorations = SysUiServiceProvider.getComponent(mRoot.getContext(),
ScreenDecorations.class);
diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
index 05665b5..835ffc9 100644
--- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
@@ -16,6 +16,7 @@
package com.android.systemui.colorextraction;
+import android.annotation.ColorInt;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.content.Context;
@@ -113,7 +114,11 @@
super.onColorsChanged(colors, which);
if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
+ @ColorInt int oldColor = mWpHiddenColors.getMainColor();
updateDefaultGradients(colors);
+ if (oldColor != mWpHiddenColors.getMainColor()) {
+ triggerColorsChanged(WallpaperManager.FLAG_SYSTEM);
+ }
}
}
@@ -121,6 +126,7 @@
public void onUiModeChanged() {
WallpaperColors systemColors = getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
updateDefaultGradients(systemColors);
+ triggerColorsChanged(WallpaperManager.FLAG_SYSTEM);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 8647200..dc97754 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -551,7 +551,7 @@
private class EmergencyDialerAction extends EmergencyAction {
private EmergencyDialerAction() {
- super(R.drawable.ic_faster_emergency,
+ super(com.android.systemui.R.drawable.ic_emergency_star,
R.string.global_action_emergency);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index ec6cfe9..9616f0a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1930,12 +1930,6 @@
+ " --> flags=0x" + Integer.toHexString(flags));
}
- // TODO(b/113914868): investigation log for disappearing home button
- Log.d(TAG, "adjustStatusBarLocked (b/113914868): flags=" + flags
- + "mShowing=" + mShowing + " mStatusBarManager=" + mStatusBarManager
- + " mOccluded=" + mOccluded + " isSecure=" + isSecure()
- + " force=" + forceHideHomeRecentsButtons);
-
mStatusBarManager.disable(flags);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
deleted file mode 100644
index a5a915b..0000000
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ /dev/null
@@ -1,110 +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.systemui.privacy
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.Gravity
-import android.view.ViewGroup
-import android.widget.FrameLayout
-import android.widget.ImageView
-import android.widget.LinearLayout
-import com.android.systemui.R
-
-class OngoingPrivacyChip @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyleAttrs: Int = 0,
- defStyleRes: Int = 0
-) : LinearLayout(context, attrs, defStyleAttrs, defStyleRes) {
-
- private val iconMarginExpanded = context.resources.getDimensionPixelSize(
- R.dimen.ongoing_appops_chip_icon_margin_expanded)
- private val iconMarginCollapsed = context.resources.getDimensionPixelSize(
- R.dimen.ongoing_appops_chip_icon_margin_collapsed)
- private val iconSize =
- context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
- private val iconColor = context.resources.getColor(
- R.color.status_bar_clock_color, context.theme)
- private val sidePadding =
- context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding)
- private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg)
- private lateinit var iconsContainer: LinearLayout
- private lateinit var back: FrameLayout
- var expanded = false
- set(value) {
- if (value != field) {
- field = value
- updateView()
- }
- }
-
- var builder = PrivacyDialogBuilder(context, emptyList<PrivacyItem>())
- var privacyList = emptyList<PrivacyItem>()
- set(value) {
- field = value
- builder = PrivacyDialogBuilder(context, value)
- updateView()
- }
-
- override fun onFinishInflate() {
- super.onFinishInflate()
-
- back = findViewById(R.id.background)
- iconsContainer = findViewById(R.id.icons_container)
- }
-
- // Should only be called if the builder icons or app changed
- private fun updateView() {
- back.background = if (expanded) backgroundDrawable else null
- val padding = if (expanded) sidePadding else 0
- back.setPaddingRelative(padding, 0, padding, 0)
- fun setIcons(dialogBuilder: PrivacyDialogBuilder, iconsContainer: ViewGroup) {
- iconsContainer.removeAllViews()
- dialogBuilder.generateIcons().forEachIndexed { i, it ->
- it.mutate()
- it.setTint(iconColor)
- val image = ImageView(context).apply {
- setImageDrawable(it)
- scaleType = ImageView.ScaleType.CENTER_INSIDE
- }
- iconsContainer.addView(image, iconSize, iconSize)
- if (i != 0) {
- val lp = image.layoutParams as MarginLayoutParams
- lp.marginStart = if (expanded) iconMarginExpanded else iconMarginCollapsed
- image.layoutParams = lp
- }
- }
- }
-
- if (!privacyList.isEmpty()) {
- generateContentDescription()
- setIcons(builder, iconsContainer)
- val lp = iconsContainer.layoutParams as FrameLayout.LayoutParams
- lp.gravity = Gravity.CENTER_VERTICAL or
- (if (expanded) Gravity.CENTER_HORIZONTAL else Gravity.END)
- iconsContainer.layoutParams = lp
- } else {
- iconsContainer.removeAllViews()
- }
- requestLayout()
- }
-
- private fun generateContentDescription() {
- val typesText = builder.joinTypes()
- contentDescription = context.getString(
- R.string.ongoing_privacy_chip_content_multiple_apps, typesText)
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
deleted file mode 100644
index d08a373..0000000
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogBuilder.kt
+++ /dev/null
@@ -1,56 +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.systemui.privacy
-
-import android.content.Context
-import android.graphics.drawable.Drawable
-import com.android.systemui.R
-
-class PrivacyDialogBuilder(private val context: Context, itemsList: List<PrivacyItem>) {
-
- val appsAndTypes: List<Pair<PrivacyApplication, List<PrivacyType>>>
- val types: List<PrivacyType>
- private val separator = context.getString(R.string.ongoing_privacy_dialog_separator)
- private val lastSeparator = context.getString(R.string.ongoing_privacy_dialog_last_separator)
-
- init {
- appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
- .toList()
- .sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps
- { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest)
- types = itemsList.map { it.privacyType }.distinct().sorted()
- }
-
- fun generateIconsForApp(types: List<PrivacyType>): List<Drawable> {
- return types.sorted().map { it.getIcon(context) }
- }
-
- fun generateIcons() = types.map { it.getIcon(context) }
-
- private fun <T> List<T>.joinWithAnd(): StringBuilder {
- return subList(0, size - 1).joinTo(StringBuilder(), separator = separator).apply {
- append(lastSeparator)
- append(this@joinWithAnd.last())
- }
- }
-
- fun joinTypes(): String {
- return when (types.size) {
- 0 -> ""
- 1 -> types[0].getName(context)
- else -> types.map { it.getName(context) }.joinWithAnd().toString()
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
deleted file mode 100644
index 2909424..0000000
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
+++ /dev/null
@@ -1,80 +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.systemui.privacy
-
-import android.content.Context
-import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager
-import android.graphics.drawable.Drawable
-import android.os.UserHandle
-import android.util.IconDrawableFactory
-import com.android.systemui.R
-
-typealias Privacy = PrivacyType
-
-enum class PrivacyType(private val nameId: Int, val iconId: Int) {
- // This is uses the icons used by the corresponding permission groups in the AndroidManifest
- TYPE_CAMERA(R.string.privacy_type_camera,
- com.android.internal.R.drawable.perm_group_camera),
- TYPE_MICROPHONE(R.string.privacy_type_microphone,
- com.android.internal.R.drawable.perm_group_microphone),
- TYPE_LOCATION(R.string.privacy_type_location,
- com.android.internal.R.drawable.perm_group_location);
-
- fun getName(context: Context) = context.resources.getString(nameId)
-
- fun getIcon(context: Context) = context.resources.getDrawable(iconId, context.theme)
-}
-
-data class PrivacyItem(
- val privacyType: PrivacyType,
- val application: PrivacyApplication
-)
-
-data class PrivacyApplication(val packageName: String, val uid: Int, val context: Context)
- : Comparable<PrivacyApplication> {
-
- override fun compareTo(other: PrivacyApplication): Int {
- return applicationName.compareTo(other.applicationName)
- }
-
- private val applicationInfo: ApplicationInfo? by lazy {
- try {
- val userHandle = UserHandle.getUserHandleForUid(uid)
- context.createPackageContextAsUser(packageName, 0, userHandle).getPackageManager()
- .getApplicationInfo(packageName, 0)
- } catch (_: PackageManager.NameNotFoundException) {
- null
- }
- }
- val icon: Drawable by lazy {
- applicationInfo?.let {
- try {
- val iconFactory = IconDrawableFactory.newInstance(context, true)
- iconFactory.getBadgedIcon(it, UserHandle.getUserId(uid))
- } catch (_: Exception) {
- null
- }
- } ?: context.getDrawable(android.R.drawable.sym_def_app_icon)
- }
-
- val applicationName: String by lazy {
- applicationInfo?.let {
- context.packageManager.getApplicationLabel(it) as String
- } ?: packageName
- }
-
- override fun toString() = "PrivacyApplication(packageName=$packageName, uid=$uid)"
-}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
deleted file mode 100644
index 82a2c1f..0000000
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ /dev/null
@@ -1,294 +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.systemui.privacy
-
-import android.app.ActivityManager
-import android.app.AppOpsManager
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.os.Handler
-import android.os.Looper
-import android.os.Message
-import android.os.UserHandle
-import android.os.UserManager
-import android.provider.DeviceConfig
-import com.android.internal.annotations.VisibleForTesting
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
-import com.android.systemui.Dependency.BG_HANDLER_NAME
-import com.android.systemui.Dependency.MAIN_HANDLER_NAME
-import com.android.systemui.R
-import com.android.systemui.appops.AppOpItem
-import com.android.systemui.appops.AppOpsController
-import com.android.systemui.Dumpable
-import java.io.FileDescriptor
-import java.io.PrintWriter
-import java.lang.ref.WeakReference
-import javax.inject.Inject
-import javax.inject.Named
-import javax.inject.Singleton
-
-fun isPermissionsHubEnabled() = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
-
-@Singleton
-class PrivacyItemController @Inject constructor(
- val context: Context,
- private val appOpsController: AppOpsController,
- @Named(MAIN_HANDLER_NAME) private val uiHandler: Handler,
- @Named(BG_HANDLER_NAME) private val bgHandler: Handler
-) : Dumpable {
-
- @VisibleForTesting
- internal companion object {
- val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
- AppOpsManager.OP_RECORD_AUDIO,
- AppOpsManager.OP_COARSE_LOCATION,
- AppOpsManager.OP_FINE_LOCATION)
- val intents = listOf(Intent.ACTION_USER_FOREGROUND,
- Intent.ACTION_MANAGED_PROFILE_ADDED,
- Intent.ACTION_MANAGED_PROFILE_REMOVED)
- const val TAG = "PrivacyItemController"
- const val SYSTEM_UID = 1000
- const val MSG_ADD_CALLBACK = 0
- const val MSG_REMOVE_CALLBACK = 1
- const val MSG_UPDATE_LISTENING_STATE = 2
- }
-
- @VisibleForTesting
- internal var privacyList = emptyList<PrivacyItem>()
- @Synchronized get() = field.toList() // Returns a shallow copy of the list
- @Synchronized set
-
- private val userManager = context.getSystemService(UserManager::class.java)
- private var currentUserIds = emptyList<Int>()
- private var listening = false
- val systemApp =
- PrivacyApplication(context.getString(R.string.device_services), SYSTEM_UID, context)
- private val callbacks = mutableListOf<WeakReference<Callback>>()
- private val messageHandler = H(WeakReference(this), uiHandler.looper)
-
- private val notifyChanges = Runnable {
- val list = privacyList
- callbacks.forEach { it.get()?.privacyChanged(list) }
- }
-
- private val updateListAndNotifyChanges = Runnable {
- updatePrivacyList()
- uiHandler.post(notifyChanges)
- }
-
- private var indicatorsAvailable = isPermissionsHubEnabled()
- @VisibleForTesting
- internal val devicePropertyChangedListener =
- object : DeviceConfig.OnPropertyChangedListener {
- override fun onPropertyChanged(namespace: String, name: String, value: String?) {
- if (DeviceConfig.NAMESPACE_PRIVACY.equals(namespace) &&
- SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED.equals(name)) {
- indicatorsAvailable = java.lang.Boolean.parseBoolean(value)
- messageHandler.removeMessages(MSG_UPDATE_LISTENING_STATE)
- messageHandler.sendEmptyMessage(MSG_UPDATE_LISTENING_STATE)
- }
- }
- }
-
- private val cb = object : AppOpsController.Callback {
- override fun onActiveStateChanged(
- code: Int,
- uid: Int,
- packageName: String,
- active: Boolean
- ) {
- val userId = UserHandle.getUserId(uid)
- if (userId in currentUserIds) {
- update(false)
- }
- }
- }
-
- @VisibleForTesting
- internal var userSwitcherReceiver = Receiver()
- set(value) {
- context.unregisterReceiver(field)
- field = value
- registerReceiver()
- }
-
- init {
- DeviceConfig.addOnPropertyChangedListener(
- DeviceConfig.NAMESPACE_PRIVACY, context.mainExecutor, devicePropertyChangedListener)
- }
-
- private fun unregisterReceiver() {
- context.unregisterReceiver(userSwitcherReceiver)
- }
-
- private fun registerReceiver() {
- context.registerReceiverAsUser(userSwitcherReceiver, UserHandle.ALL, IntentFilter().apply {
- intents.forEach {
- addAction(it)
- }
- }, null, null)
- }
-
- private fun update(updateUsers: Boolean) {
- if (updateUsers) {
- val currentUser = ActivityManager.getCurrentUser()
- currentUserIds = userManager.getProfiles(currentUser).map { it.id }
- }
- bgHandler.post(updateListAndNotifyChanges)
- }
-
- /**
- * Updates listening status based on whether there are callbacks and the indicators are enabled
- *
- * This is only called from private (add/remove)Callback and from the config listener, all in
- * main thread.
- */
- private fun setListeningState() {
- val listen = !callbacks.isEmpty() and indicatorsAvailable
- if (listening == listen) return
- listening = listen
- if (listening) {
- appOpsController.addCallback(OPS, cb)
- registerReceiver()
- update(true)
- } else {
- appOpsController.removeCallback(OPS, cb)
- unregisterReceiver()
- // Make sure that we remove all indicators and notify listeners if we are not
- // listening anymore due to indicators being disabled
- update(false)
- }
- }
-
- private fun addCallback(callback: WeakReference<Callback>) {
- callbacks.add(callback)
- if (callbacks.isNotEmpty() && !listening) {
- messageHandler.removeMessages(MSG_UPDATE_LISTENING_STATE)
- messageHandler.sendEmptyMessage(MSG_UPDATE_LISTENING_STATE)
- }
- // Notify this callback if we didn't set to listening
- else if (listening) uiHandler.post(NotifyChangesToCallback(callback.get(), privacyList))
- }
-
- private fun removeCallback(callback: WeakReference<Callback>) {
- // Removes also if the callback is null
- callbacks.removeIf { it.get()?.equals(callback.get()) ?: true }
- if (callbacks.isEmpty()) {
- messageHandler.removeMessages(MSG_UPDATE_LISTENING_STATE)
- messageHandler.sendEmptyMessage(MSG_UPDATE_LISTENING_STATE)
- }
- }
-
- fun addCallback(callback: Callback) {
- messageHandler.obtainMessage(MSG_ADD_CALLBACK, callback).sendToTarget()
- }
-
- fun removeCallback(callback: Callback) {
- messageHandler.obtainMessage(MSG_REMOVE_CALLBACK, callback).sendToTarget()
- }
-
- private fun updatePrivacyList() {
- if (!listening) {
- privacyList = emptyList()
- return
- }
- val list = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
- .mapNotNull { toPrivacyItem(it) }.distinct()
- privacyList = list
- }
-
- private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
- val type: PrivacyType = when (appOpItem.code) {
- AppOpsManager.OP_CAMERA -> PrivacyType.TYPE_CAMERA
- AppOpsManager.OP_COARSE_LOCATION -> PrivacyType.TYPE_LOCATION
- AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION
- AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
- else -> return null
- }
- if (appOpItem.uid == SYSTEM_UID) return PrivacyItem(type, systemApp)
- val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid, context)
- return PrivacyItem(type, app)
- }
-
- // Used by containing class to get notified of changes
- interface Callback {
- fun privacyChanged(privacyItems: List<PrivacyItem>)
- }
-
- internal inner class Receiver : BroadcastReceiver() {
- override fun onReceive(context: Context?, intent: Intent?) {
- if (intent?.action in intents) {
- update(true)
- }
- }
- }
-
- private class NotifyChangesToCallback(
- private val callback: Callback?,
- private val list: List<PrivacyItem>
- ) : Runnable {
- override fun run() {
- callback?.privacyChanged(list)
- }
- }
-
- override fun dump(fd: FileDescriptor?, pw: PrintWriter?, args: Array<out String>?) {
- pw?.println("PrivacyItemController state:")
- pw?.println(" Listening: $listening")
- pw?.println(" Current user ids: $currentUserIds")
- pw?.println(" Privacy Items:")
- privacyList.forEach {
- pw?.print(" ")
- pw?.println(it.toString())
- }
- pw?.println(" Callbacks:")
- callbacks.forEach {
- it.get()?.let {
- pw?.print(" ")
- pw?.println(it.toString())
- }
- }
- }
-
- private class H(
- private val outerClass: WeakReference<PrivacyItemController>,
- looper: Looper
- ) : Handler(looper) {
- override fun handleMessage(msg: Message) {
- super.handleMessage(msg)
- when (msg.what) {
- MSG_UPDATE_LISTENING_STATE -> outerClass.get()?.setListeningState()
-
- MSG_ADD_CALLBACK -> {
- if (msg.obj !is PrivacyItemController.Callback) return
- outerClass.get()?.addCallback(
- WeakReference(msg.obj as PrivacyItemController.Callback))
- }
-
- MSG_REMOVE_CALLBACK -> {
- if (msg.obj !is PrivacyItemController.Callback) return
- outerClass.get()?.removeCallback(
- WeakReference(msg.obj as PrivacyItemController.Callback))
- }
- else -> {}
- }
- }
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 9842748..d59e251 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -32,30 +32,24 @@
import android.graphics.Rect;
import android.media.AudioManager;
import android.os.Handler;
-import android.os.Looper;
import android.provider.AlarmClock;
-import android.provider.DeviceConfig;
import android.provider.Settings;
import android.service.notification.ZenModeConfig;
import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
-import android.util.StatsLog;
import android.view.ContextThemeWrapper;
import android.view.DisplayCutout;
import android.view.View;
import android.view.WindowInsets;
import android.widget.FrameLayout;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.RelativeLayout;
-import android.widget.Space;
import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.settingslib.Utils;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.DualToneHandler;
@@ -63,11 +57,6 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.privacy.OngoingPrivacyChip;
-import com.android.systemui.privacy.PrivacyDialogBuilder;
-import com.android.systemui.privacy.PrivacyItem;
-import com.android.systemui.privacy.PrivacyItemController;
-import com.android.systemui.privacy.PrivacyItemControllerKt;
import com.android.systemui.qs.QSDetail.Callback;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -78,8 +67,6 @@
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.ZenModeController;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Locale;
import java.util.Objects;
@@ -121,7 +108,6 @@
private TintedIconManager mIconManager;
private TouchAnimator mStatusIconsAlphaAnimator;
private TouchAnimator mHeaderTextContainerAlphaAnimator;
- private TouchAnimator mPrivacyChipAlphaAnimator;
private DualToneHandler mDualToneHandler;
private View mSystemIconsView;
@@ -141,12 +127,7 @@
private View mRingerContainer;
private Clock mClockView;
private DateView mDateView;
- private OngoingPrivacyChip mPrivacyChip;
- private Space mSpace;
private BatteryMeterView mBatteryRemainingIcon;
- private boolean mPermissionsHubEnabled;
-
- private PrivacyItemController mPrivacyItemController;
private final BroadcastReceiver mRingerReceiver = new BroadcastReceiver() {
@Override
@@ -156,41 +137,17 @@
}
};
private boolean mHasTopCutout = false;
- private boolean mPrivacyChipLogged = false;
-
- private final DeviceConfig.OnPropertyChangedListener mPropertyListener =
- new DeviceConfig.OnPropertyChangedListener() {
- @Override
- public void onPropertyChanged(String namespace, String name, String value) {
- if (DeviceConfig.NAMESPACE_PRIVACY.equals(namespace)
- && SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED.equals(
- name)) {
- mPermissionsHubEnabled = Boolean.valueOf(value);
- StatusIconContainer iconContainer = findViewById(R.id.statusIcons);
- iconContainer.setIgnoredSlots(getIgnoredIconSlots());
- }
- }
- };
-
- private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
- @Override
- public void privacyChanged(List<PrivacyItem> privacyItems) {
- mPrivacyChip.setPrivacyList(privacyItems);
- setChipVisibility(!privacyItems.isEmpty());
- }
- };
@Inject
public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
NextAlarmController nextAlarmController, ZenModeController zenModeController,
StatusBarIconController statusBarIconController,
- ActivityStarter activityStarter, PrivacyItemController privacyItemController) {
+ ActivityStarter activityStarter) {
super(context, attrs);
mAlarmController = nextAlarmController;
mZenController = zenModeController;
mStatusBarIconController = statusBarIconController;
mActivityStarter = activityStarter;
- mPrivacyItemController = privacyItemController;
mDualToneHandler = new DualToneHandler(
new ContextThemeWrapper(context, R.style.QSHeaderTheme));
}
@@ -203,8 +160,6 @@
mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons);
mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons);
StatusIconContainer iconContainer = findViewById(R.id.statusIcons);
- // Ignore privacy icons because they show in the space above QQS
- iconContainer.addIgnoredSlots(getIgnoredIconSlots());
iconContainer.setShouldRestrictIcons(false);
mIconManager = new TintedIconManager(iconContainer);
@@ -218,9 +173,6 @@
mRingerModeIcon = findViewById(R.id.ringer_mode_icon);
mRingerModeTextView = findViewById(R.id.ringer_mode_text);
mRingerContainer = findViewById(R.id.ringer_container);
- mRingerContainer.setOnClickListener(this::onClick);
- mPrivacyChip = findViewById(R.id.privacy_chip);
- mPrivacyChip.setOnClickListener(this::onClick);
mCarrierGroup = findViewById(R.id.carrier_group);
@@ -243,7 +195,6 @@
mClockView = findViewById(R.id.clock);
mClockView.setOnClickListener(this);
mDateView = findViewById(R.id.date);
- mSpace = findViewById(R.id.space);
// Tint for the battery icons are handled in setupHost()
mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
@@ -254,26 +205,6 @@
mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
mRingerModeTextView.setSelected(true);
mNextAlarmTextView.setSelected(true);
-
- mPermissionsHubEnabled = PrivacyItemControllerKt.isPermissionsHubEnabled();
- // Change the ignored slots when DeviceConfig flag changes
- DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
- mContext.getMainExecutor(), mPropertyListener);
-
- }
-
- private List<String> getIgnoredIconSlots() {
- ArrayList<String> ignored = new ArrayList<>();
- ignored.add(mContext.getResources().getString(
- com.android.internal.R.string.status_bar_camera));
- ignored.add(mContext.getResources().getString(
- com.android.internal.R.string.status_bar_microphone));
- if (mPermissionsHubEnabled) {
- ignored.add(mContext.getResources().getString(
- com.android.internal.R.string.status_bar_location));
- }
-
- return ignored;
}
private void updateStatusText() {
@@ -287,21 +218,6 @@
}
}
- private void setChipVisibility(boolean chipVisible) {
- if (chipVisible && mPermissionsHubEnabled) {
- mPrivacyChip.setVisibility(View.VISIBLE);
- // Makes sure that the chip is logged as viewed at most once each time QS is opened
- // mListening makes sure that the callback didn't return after the user closed QS
- if (!mPrivacyChipLogged && mListening) {
- mPrivacyChipLogged = true;
- StatsLog.write(StatsLog.PRIVACY_INDICATORS_INTERACTED,
- StatsLog.PRIVACY_INDICATORS_INTERACTED__TYPE__CHIP_VIEWED);
- }
- } else {
- mPrivacyChip.setVisibility(View.GONE);
- }
- }
-
private boolean updateRingerStatus() {
boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
CharSequence originalRingerText = mRingerModeTextView.getText();
@@ -408,7 +324,6 @@
updateStatusIconAlphaAnimator();
updateHeaderTextContainerAlphaAnimator();
- updatePrivacyChipAlphaAnimator();
}
private void updateStatusIconAlphaAnimator() {
@@ -423,12 +338,6 @@
.build();
}
- private void updatePrivacyChipAlphaAnimator() {
- mPrivacyChipAlphaAnimator = new TouchAnimator.Builder()
- .addFloat(mPrivacyChip, "alpha", 1, 0, 1)
- .build();
- }
-
public void setExpanded(boolean expanded) {
if (mExpanded == expanded) return;
mExpanded = expanded;
@@ -461,10 +370,11 @@
if (mHeaderTextContainerAlphaAnimator != null) {
mHeaderTextContainerAlphaAnimator.setPosition(keyguardExpansionFraction);
- }
- if (mPrivacyChipAlphaAnimator != null) {
- mPrivacyChip.setExpanded(expansionFraction > 0.5);
- mPrivacyChipAlphaAnimator.setPosition(keyguardExpansionFraction);
+ if (keyguardExpansionFraction > 0) {
+ mHeaderTextContainerView.setVisibility(VISIBLE);
+ } else {
+ mHeaderTextContainerView.setVisibility(INVISIBLE);
+ }
}
}
@@ -498,21 +408,6 @@
mSystemIconsView.setPadding(padding.first, 0, padding.second, 0);
}
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams();
- if (cutout != null) {
- Rect topCutout = cutout.getBoundingRectTop();
- if (topCutout.isEmpty()) {
- mHasTopCutout = false;
- lp.width = 0;
- mSpace.setVisibility(View.GONE);
- } else {
- mHasTopCutout = true;
- lp.width = topCutout.width();
- mSpace.setVisibility(View.VISIBLE);
- }
- }
- mSpace.setLayoutParams(lp);
- setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
return super.onApplyWindowInsets(insets);
}
@@ -537,13 +432,10 @@
mAlarmController.addCallback(this);
mContext.registerReceiver(mRingerReceiver,
new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
- mPrivacyItemController.addCallback(mPICCallback);
} else {
mZenController.removeCallback(this);
mAlarmController.removeCallback(this);
- mPrivacyItemController.removeCallback(mPICCallback);
mContext.unregisterReceiver(mRingerReceiver);
- mPrivacyChipLogged = false;
}
}
@@ -561,18 +453,6 @@
mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
AlarmClock.ACTION_SHOW_ALARMS), 0);
}
- } else if (v == mPrivacyChip) {
- // Makes sure that the builder is grabbed as soon as the chip is pressed
- PrivacyDialogBuilder builder = mPrivacyChip.getBuilder();
- if (builder.getAppsAndTypes().size() == 0) return;
- Handler mUiHandler = new Handler(Looper.getMainLooper());
- StatsLog.write(StatsLog.PRIVACY_INDICATORS_INTERACTED,
- StatsLog.PRIVACY_INDICATORS_INTERACTED__TYPE__CHIP_CLICKED);
- mUiHandler.post(() -> {
- mActivityStarter.postStartActivityDismissingKeyguard(
- new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0);
- mHost.collapsePanels();
- });
} else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) {
mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
Settings.ACTION_SOUND_SETTINGS), 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 25bde3b..812c1bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -741,13 +741,5 @@
updateIndication(false);
}
}
-
- @Override
- public void onKeyguardBouncerChanged(boolean bouncer) {
- if (mLockIcon == null) {
- return;
- }
- mLockIcon.setBouncerVisible(bouncer);
- }
- };
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 799876d..1779b32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -294,17 +294,17 @@
int backgroundTop = 0;
int clipTopAmount = 0;
float firstElementRoundness = 0.0f;
- ExpandableNotificationRow previousRow = null;
+ ActivatableNotificationView previousRow = null;
for (int i = 0; i < mHostLayout.getChildCount(); i++) {
ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
- if (!(child instanceof ExpandableNotificationRow)
- || child.getVisibility() == GONE) {
+ if (!(child instanceof ActivatableNotificationView)
+ || child.getVisibility() == GONE || child == this) {
continue;
}
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ ActivatableNotificationView row = (ActivatableNotificationView) child;
float notificationClipEnd;
boolean aboveShelf = ViewState.getFinalTranslationZ(row) > baseZHeight
|| row.isPinned();
@@ -324,43 +324,55 @@
}
int clipTop = updateNotificationClipHeight(row, notificationClipEnd, notGoneIndex);
clipTopAmount = Math.max(clipTop, clipTopAmount);
- float inShelfAmount = updateIconAppearance(row, expandAmount, scrolling, scrollingFast,
- expandingAnimated, isLastChild);
- numViewsInShelf += inShelfAmount;
- int ownColorUntinted = row.getBackgroundColorWithoutTint();
- if (rowTranslationY >= shelfStart && mNotGoneIndex == -1) {
- mNotGoneIndex = notGoneIndex;
- setTintColor(previousColor);
- setOverrideTintColor(colorTwoBefore, transitionAmount);
- } else if (mNotGoneIndex == -1) {
- colorTwoBefore = previousColor;
- transitionAmount = inShelfAmount;
- }
- if (isLastChild) {
- if (colorOfViewBeforeLast == NO_COLOR) {
+ // If the current row is an ExpandableNotificationRow, update its color, roundedness,
+ // and icon state.
+ if (row instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow expandableRow = (ExpandableNotificationRow) row;
+
+ float inShelfAmount = updateIconAppearance(expandableRow, expandAmount, scrolling,
+ scrollingFast,
+ expandingAnimated, isLastChild);
+ numViewsInShelf += inShelfAmount;
+ int ownColorUntinted = row.getBackgroundColorWithoutTint();
+ if (rowTranslationY >= shelfStart && mNotGoneIndex == -1) {
+ mNotGoneIndex = notGoneIndex;
+ setTintColor(previousColor);
+ setOverrideTintColor(colorTwoBefore, transitionAmount);
+
+ } else if (mNotGoneIndex == -1) {
+ colorTwoBefore = previousColor;
+ transitionAmount = inShelfAmount;
+ }
+ if (isLastChild) {
+ if (colorOfViewBeforeLast == NO_COLOR) {
+ colorOfViewBeforeLast = ownColorUntinted;
+ }
+ row.setOverrideTintColor(colorOfViewBeforeLast, inShelfAmount);
+ } else {
colorOfViewBeforeLast = ownColorUntinted;
+ row.setOverrideTintColor(NO_COLOR, 0 /* overrideAmount */);
}
- row.setOverrideTintColor(colorOfViewBeforeLast, inShelfAmount);
- } else {
- colorOfViewBeforeLast = ownColorUntinted;
- row.setOverrideTintColor(NO_COLOR, 0 /* overrideAmount */);
- }
- if (notGoneIndex != 0 || !aboveShelf) {
- row.setAboveShelf(false);
- }
- if (notGoneIndex == 0) {
- StatusBarIconView icon = row.getEntry().expandedIcon;
- NotificationIconContainer.IconState iconState = getIconState(icon);
- // The icon state might be null in rare cases where the notification is actually
- // added to the layout, but not to the shelf. An example are replied messages, since
- // they don't show up on AOD
- if (iconState != null && iconState.clampedAppearAmount == 1.0f) {
- // only if the first icon is fully in the shelf we want to clip to it!
- backgroundTop = (int) (row.getTranslationY() - getTranslationY());
- firstElementRoundness = row.getCurrentTopRoundness();
+ if (notGoneIndex != 0 || !aboveShelf) {
+ expandableRow.setAboveShelf(false);
}
+ if (notGoneIndex == 0) {
+ StatusBarIconView icon = expandableRow.getEntry().expandedIcon;
+ NotificationIconContainer.IconState iconState = getIconState(icon);
+ // The icon state might be null in rare cases where the notification is actually
+ // added to the layout, but not to the shelf. An example are replied messages,
+ // since they don't show up on AOD
+ if (iconState != null && iconState.clampedAppearAmount == 1.0f) {
+ // only if the first icon is fully in the shelf we want to clip to it!
+ backgroundTop = (int) (row.getTranslationY() - getTranslationY());
+ firstElementRoundness = row.getCurrentTopRoundness();
+ }
+ }
+
+ previousColor = ownColorUntinted;
+ notGoneIndex++;
}
+
if (row.isFirstInSection() && previousRow != null && previousRow.isLastInSection()) {
// If the top of the shelf is between the view before a gap and the view after a gap
// then we need to adjust the shelf's top roundness.
@@ -379,8 +391,6 @@
backgroundTop = (int) distanceToGapBottom;
}
}
- notGoneIndex++;
- previousColor = ownColorUntinted;
previousRow = row;
}
clipTransientViews();
@@ -497,7 +507,7 @@
* Update the clipping of this view.
* @return the amount that our own top should be clipped
*/
- private int updateNotificationClipHeight(ExpandableNotificationRow row,
+ private int updateNotificationClipHeight(ActivatableNotificationView row,
float notificationClipEnd, int childIndex) {
float viewEnd = row.getTranslationY() + row.getActualHeight();
boolean isPinned = (row.isPinned() || row.isHeadsUpAnimatingAway())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index e8388ce..879a8df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -355,10 +355,10 @@
// a child we're keeping around for reply!
continue;
}
- entry.setKeepInParent(true);
+ childEntry.setKeepInParent(true);
// we need to set this state earlier as otherwise we might generate some weird
// animations
- entry.removeRow();
+ childEntry.removeRow();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 396a3a7..4a6c7d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -16,18 +16,26 @@
package com.android.systemui.statusbar.notification;
+import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+
+import android.os.Handler;
+import android.os.SystemClock;
import android.view.View;
import androidx.collection.ArraySet;
+import com.android.systemui.Dumpable;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import javax.inject.Inject;
+import javax.inject.Named;
import javax.inject.Singleton;
/**
@@ -35,14 +43,19 @@
* and reorder at the right time when they are out of view.
*/
@Singleton
-public class VisualStabilityManager implements OnHeadsUpChangedListener {
+public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpable {
+
+ private static final long TEMPORARY_REORDERING_ALLOWED_DURATION = 1000;
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ private final Handler mHandler;
private NotificationPresenter mPresenter;
private boolean mPanelExpanded;
private boolean mScreenOn;
private boolean mReorderingAllowed;
+ private boolean mIsTemporaryReorderingAllowed;
+ private long mTemporaryReorderingStart;
private VisibilityLocationProvider mVisibilityLocationProvider;
private ArraySet<View> mAllowedReorderViews = new ArraySet<>();
private ArraySet<NotificationEntry> mLowPriorityReorderingViews = new ArraySet<>();
@@ -50,7 +63,12 @@
private boolean mPulsing;
@Inject
- public VisualStabilityManager(NotificationEntryManager notificationEntryManager) {
+ public VisualStabilityManager(
+ NotificationEntryManager notificationEntryManager,
+ @Named(MAIN_HANDLER_NAME) Handler handler) {
+
+ mHandler = handler;
+
notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
public void onPreEntryUpdated(NotificationEntry entry) {
@@ -114,10 +132,11 @@
}
private void updateReorderingAllowed() {
- boolean reorderingAllowed = (!mScreenOn || !mPanelExpanded) && !mPulsing;
- boolean changed = reorderingAllowed && !mReorderingAllowed;
+ boolean reorderingAllowed =
+ (!mScreenOn || !mPanelExpanded || mIsTemporaryReorderingAllowed) && !mPulsing;
+ boolean changedToTrue = reorderingAllowed && !mReorderingAllowed;
mReorderingAllowed = reorderingAllowed;
- if (changed) {
+ if (changedToTrue) {
notifyCallbacks();
}
}
@@ -180,6 +199,25 @@
}
/**
+ * Temporarily allows reordering of the entire shade for a period of 1000ms. Subsequent calls
+ * to this method will extend the timer.
+ */
+ public void temporarilyAllowReordering() {
+ mHandler.removeCallbacks(mOnTemporaryReorderingExpired);
+ mHandler.postDelayed(mOnTemporaryReorderingExpired, TEMPORARY_REORDERING_ALLOWED_DURATION);
+ if (!mIsTemporaryReorderingAllowed) {
+ mTemporaryReorderingStart = SystemClock.elapsedRealtime();
+ }
+ mIsTemporaryReorderingAllowed = true;
+ updateReorderingAllowed();
+ }
+
+ private final Runnable mOnTemporaryReorderingExpired = () -> {
+ mIsTemporaryReorderingAllowed = false;
+ updateReorderingAllowed();
+ };
+
+ /**
* Notify the visual stability manager that a new view was added and should be allowed to
* reorder next time.
*/
@@ -187,6 +225,20 @@
mAddedChildren.add(view);
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("VisualStabilityManager state:");
+ pw.print(" mIsTemporaryReorderingAllowed="); pw.println(mIsTemporaryReorderingAllowed);
+ pw.print(" mTemporaryReorderingStart="); pw.println(mTemporaryReorderingStart);
+
+ long now = SystemClock.elapsedRealtime();
+ pw.print(" Temporary reordering window has been open for ");
+ pw.print(now - (mIsTemporaryReorderingAllowed ? mTemporaryReorderingStart : now));
+ pw.println("ms");
+
+ pw.println();
+ }
+
public interface Callback {
/**
* Called when reordering is allowed again.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index a3e18ef..92c261c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -122,7 +122,6 @@
public boolean suspended;
private NotificationEntry parent; // our parent (if we're in a group)
- private ArrayList<NotificationEntry> children = new ArrayList<NotificationEntry>();
private ExpandableNotificationRow row; // the outer expanded view
private int mCachedContrastColor = COLOR_INVALID;
@@ -277,10 +276,20 @@
@Nullable
public List<NotificationEntry> getChildren() {
- if (children.size() <= 0) {
+ if (row == null) {
return null;
}
+ List<ExpandableNotificationRow> rowChildren = row.getNotificationChildren();
+ if (rowChildren == null) {
+ return null;
+ }
+
+ ArrayList<NotificationEntry> children = new ArrayList<>();
+ for (ExpandableNotificationRow child : rowChildren) {
+ children.add(child.getEntry());
+ }
+
return children;
}
@@ -740,7 +749,9 @@
if (notification == null || !notification.isClearable()) {
return false;
}
- if (children.size() > 0) {
+
+ List<NotificationEntry> children = getChildren();
+ if (children != null && children.size() > 0) {
for (int i = 0; i < children.size(); i++) {
NotificationEntry child = children.get(i);
if (!child.isClearable()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index b89b5cb..5378f90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -43,6 +43,18 @@
const val TAG = "ChannelDialogController"
+/**
+ * ChannelEditorDialogController is the controller for the dialog half-shelf
+ * that allows users to quickly turn off channels. It is launched from the NotificationInfo
+ * guts view and displays controls for toggling app notifications as well as up to 4 channels
+ * from that app like so:
+ *
+ * APP TOGGLE <on/off>
+ * - Channel from which we launched <on/off>
+ * - <on/off>
+ * - the next 3 channels sorted alphabetically for that app <on/off>
+ * - <on/off>
+ */
@Singleton
class ChannelEditorDialogController @Inject constructor(
c: Context,
@@ -58,6 +70,9 @@
private var appName: String? = null
private var onSettingsClickListener: NotificationInfo.OnSettingsClickListener? = null
+ // Caller should set this if they care about when we dismiss
+ var onFinishListener: OnChannelEditorDialogFinishedListener? = null
+
// Channels handed to us from NotificationInfo
@VisibleForTesting
internal val providedChannels = mutableListOf<NotificationChannel>()
@@ -144,6 +159,7 @@
private fun done() {
resetState()
dialog.dismiss()
+ onFinishListener?.onChannelEditorDialogFinished()
}
private fun resetState() {
@@ -223,6 +239,8 @@
dialog = Dialog(context)
dialog.window?.requestFeature(Window.FEATURE_NO_TITLE)
+ // Prevent a11y readers from reading the first element in the dialog twice
+ dialog.setTitle("\u00A0")
dialog.apply {
setContentView(R.layout.notif_half_shelf)
setCanceledOnTouchOutside(true)
@@ -240,7 +258,7 @@
findViewById<TextView>(R.id.see_more_button)?.setOnClickListener {
onSettingsClickListener?.onClick(it, null, appUid!!)
- dismiss()
+ done()
}
window?.apply {
@@ -265,3 +283,7 @@
or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
}
+
+interface OnChannelEditorDialogFinishedListener {
+ fun onChannelEditorDialogFinished()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
index 4d49760..6fe1477 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
@@ -143,7 +143,7 @@
private fun updateViews() {
val nc = channel ?: return
- channelName.text = nc.name ?: "(missing)"
+ channelName.text = nc.name ?: ""
nc.group?.let { groupId ->
channelDescription.text = controller.groupNameForId(groupId)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index f15d6b7..fe890fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -48,6 +48,7 @@
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -73,6 +74,7 @@
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private final Context mContext;
+ private final VisualStabilityManager mVisualStabilityManager;
private final AccessibilityManager mAccessibilityManager;
// Dependencies:
@@ -96,8 +98,11 @@
protected String mKeyToRemoveOnGutsClosed;
@Inject
- public NotificationGutsManager(Context context) {
+ public NotificationGutsManager(
+ Context context,
+ VisualStabilityManager visualStabilityManager) {
mContext = context;
+ mVisualStabilityManager = visualStabilityManager;
mAccessibilityManager = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
}
@@ -304,6 +309,7 @@
notificationInfoView.bindNotification(
pmUser,
iNotificationManager,
+ mVisualStabilityManager,
packageName,
row.getEntry().channel,
row.getUniqueChannels(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index 1dc96b8..0f6740d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -65,6 +65,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
import java.lang.annotation.Retention;
@@ -104,6 +105,7 @@
private INotificationManager mINotificationManager;
private PackageManager mPm;
private MetricsLogger mMetricsLogger;
+ private VisualStabilityManager mVisualStabilityManager;
private ChannelEditorDialogController mChannelEditorDialogController;
private String mPackageName;
@@ -159,13 +161,13 @@
// used by standard ui
private OnClickListener mOnDismissSettings = v -> {
mPressedApply = true;
- closeControls(v);
+ closeControls(v, true);
};
// used by blocking helper
private OnClickListener mOnKeepShowing = v -> {
mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
- closeControls(v);
+ closeControls(v, true);
mMetricsLogger.write(getLogMaker().setCategory(
MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
.setType(MetricsEvent.TYPE_ACTION)
@@ -244,6 +246,7 @@
void bindNotification(
final PackageManager pm,
final INotificationManager iNotificationManager,
+ final VisualStabilityManager visualStabilityManager,
final String pkg,
final NotificationChannel notificationChannel,
final Set<NotificationChannel> uniqueChannelsInRow,
@@ -256,7 +259,7 @@
int importance,
boolean wasShownHighPriority)
throws RemoteException {
- bindNotification(pm, iNotificationManager, pkg, notificationChannel,
+ bindNotification(pm, iNotificationManager, visualStabilityManager, pkg, notificationChannel,
uniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
onAppSettingsClick, isDeviceProvisioned, isNonblockable,
false /* isBlockingHelper */,
@@ -266,6 +269,7 @@
public void bindNotification(
PackageManager pm,
INotificationManager iNotificationManager,
+ VisualStabilityManager visualStabilityManager,
String pkg,
NotificationChannel notificationChannel,
Set<NotificationChannel> uniqueChannelsInRow,
@@ -281,6 +285,7 @@
throws RemoteException {
mINotificationManager = iNotificationManager;
mMetricsLogger = Dependency.get(MetricsLogger.class);
+ mVisualStabilityManager = visualStabilityManager;
mChannelEditorDialogController = Dependency.get(ChannelEditorDialogController.class);
mPackageName = pkg;
mUniqueChannelsInRow = uniqueChannelsInRow;
@@ -445,6 +450,8 @@
if (mChannelEditorDialogController != null) {
mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid,
mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener);
+ mChannelEditorDialogController.setOnFinishListener(
+ () -> closeControls(this, false));
mChannelEditorDialogController.show();
}
});
@@ -537,6 +544,7 @@
new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
mStartingChannelImportance, newImportance));
+ mVisualStabilityManager.temporarilyAllowReordering();
}
}
@@ -725,7 +733,7 @@
* {@link #swapContent(boolean, boolean)} for where undo is handled.
*/
@VisibleForTesting
- void closeControls(View v) {
+ void closeControls(View v, boolean save) {
int[] parentLoc = new int[2];
int[] targetLoc = new int[2];
mGutsContainer.getLocationOnScreen(parentLoc);
@@ -734,7 +742,7 @@
final int centerY = v.getHeight() / 2;
final int x = targetLoc[0] - parentLoc[0] + centerX;
final int y = targetLoc[1] - parentLoc[1] + centerY;
- mGutsContainer.closeControls(x, y, true /* save */, false /* force */);
+ mGutsContainer.closeControls(x, y, save, false /* force */);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
index ee2dacd6..20e8b73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java
@@ -18,6 +18,7 @@
import static com.android.systemui.Dependency.MAIN_HANDLER;
+import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -28,7 +29,6 @@
import android.metrics.LogMaker;
import android.os.Handler;
import android.text.format.DateUtils;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewStub;
@@ -52,7 +52,6 @@
*/
public class NotificationMediaTemplateViewWrapper extends NotificationTemplateViewWrapper {
- private static final String TAG = "NotificationMediaTVW";
private static final long PROGRESS_UPDATE_INTERVAL = 1000; // 1s
private static final String COMPACT_MEDIA_TAG = "media";
private final Handler mHandler = Dependency.get(MAIN_HANDLER);
@@ -63,6 +62,7 @@
private TextView mSeekBarTotalTime;
private long mDuration = 0;
private MediaController mMediaController;
+ private MediaMetadata mMediaMetadata;
private NotificationMediaManager mMediaManager;
private View mSeekBarView;
private Context mContext;
@@ -81,7 +81,7 @@
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
- if (mMediaController != null && canSeekMedia()) {
+ if (mMediaController != null) {
mMediaController.getTransportControls().seekTo(mSeekBar.getProgress());
mMetricsLogger.write(newLog(MetricsEvent.TYPE_UPDATE));
}
@@ -96,16 +96,28 @@
}
@Override
- public void onPlaybackStateChanged(PlaybackState state) {
+ public void onPlaybackStateChanged(@Nullable PlaybackState state) {
+ if (state == null) {
+ return;
+ }
+
if (state.getState() != PlaybackState.STATE_PLAYING) {
// Update the UI once, in case playback info changed while we were paused
- mUpdatePlaybackUi.run();
+ updatePlaybackUi(state);
clearTimer();
} else if (mSeekBarTimer == null && mSeekBarView != null
&& mSeekBarView.getVisibility() != View.GONE) {
startTimer();
}
}
+
+ @Override
+ public void onMetadataChanged(@Nullable MediaMetadata metadata) {
+ if (mMediaMetadata == null || !mMediaMetadata.equals(metadata)) {
+ mMediaMetadata = metadata;
+ updateDuration();
+ }
+ }
};
protected NotificationMediaTemplateViewWrapper(Context ctx, View view,
@@ -140,12 +152,11 @@
controllerUpdated = true;
}
- if (mMediaController.getMetadata() != null) {
- long duration = mMediaController.getMetadata().getLong(
- MediaMetadata.METADATA_KEY_DURATION);
+ mMediaMetadata = mMediaController.getMetadata();
+ if (mMediaMetadata != null) {
+ long duration = mMediaMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
if (duration <= 0) {
// Don't include the seekbar if this is a livestream
- Log.d(TAG, "removing seekbar");
if (mSeekBarView != null && mSeekBarView.getVisibility() != View.GONE) {
mSeekBarView.setVisibility(View.GONE);
mMetricsLogger.write(newLog(MetricsEvent.TYPE_CLOSE));
@@ -156,12 +167,12 @@
mMetricsLogger.write(newLog(MetricsEvent.TYPE_CLOSE));
}
return;
- } else {
+ } else if (mSeekBarView != null && mSeekBarView.getVisibility() == View.GONE) {
// Otherwise, make sure the seekbar is visible
- if (mSeekBarView != null && mSeekBarView.getVisibility() == View.GONE) {
- mSeekBarView.setVisibility(View.VISIBLE);
- mMetricsLogger.write(newLog(MetricsEvent.TYPE_OPEN));
- }
+ mSeekBarView.setVisibility(View.VISIBLE);
+ mMetricsLogger.write(newLog(MetricsEvent.TYPE_OPEN));
+ updateDuration();
+ startTimer();
}
}
@@ -181,13 +192,13 @@
mSeekBarTotalTime = mSeekBarView.findViewById(R.id.notification_media_total_time);
if (mSeekBarTimer == null) {
- if (canSeekMedia()) {
+ if (mMediaController != null && canSeekMedia(mMediaController.getPlaybackState())) {
// Log initial state, since it will not be updated
mMetricsLogger.write(newLog(MetricsEvent.TYPE_DETAIL, 1));
} else {
setScrubberVisible(false);
}
-
+ updateDuration();
startTimer();
mMediaController.registerCallback(mMediaCallback);
}
@@ -201,7 +212,7 @@
mSeekBarTimer.schedule(new TimerTask() {
@Override
public void run() {
- mHandler.post(mUpdatePlaybackUi);
+ mHandler.post(mOnUpdateTimerTick);
}
}, 0, PROGRESS_UPDATE_INTERVAL);
}
@@ -215,14 +226,12 @@
}
}
- private boolean canSeekMedia() {
- if (mMediaController == null || mMediaController.getPlaybackState() == null) {
- Log.d(TAG, "Cannot seek media because the controller is invalid");
+ private boolean canSeekMedia(@Nullable PlaybackState state) {
+ if (state == null) {
return false;
}
- long actions = mMediaController.getPlaybackState().getActions();
- Log.d(TAG, "Playback state actions are " + actions);
+ long actions = state.getActions();
return ((actions & PlaybackState.ACTION_SEEK_TO) != 0);
}
@@ -236,39 +245,44 @@
mMetricsLogger.write(newLog(MetricsEvent.TYPE_DETAIL, isVisible ? 1 : 0));
}
- protected final Runnable mUpdatePlaybackUi = new Runnable() {
+ private void updateDuration() {
+ if (mMediaMetadata != null && mSeekBar != null) {
+ long duration = mMediaMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
+ if (mDuration != duration) {
+ mDuration = duration;
+ mSeekBar.setMax((int) mDuration);
+ mSeekBarTotalTime.setText(millisecondsToTimeString(duration));
+ }
+ }
+ }
+
+ protected final Runnable mOnUpdateTimerTick = new Runnable() {
@Override
public void run() {
if (mMediaController != null && mSeekBar != null) {
- MediaMetadata metadata = mMediaController.getMetadata();
PlaybackState playbackState = mMediaController.getPlaybackState();
- if (metadata != null && playbackState != null) {
- long position = playbackState.getPosition();
- long duration = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
-
- if (mDuration != duration) {
- mDuration = duration;
- mSeekBar.setMax((int) mDuration);
- mSeekBarTotalTime.setText(millisecondsToTimeString(duration));
- }
- mSeekBar.setProgress((int) position);
-
- mSeekBarElapsedTime.setText(millisecondsToTimeString(position));
-
- // Update scrubber in case available actions have changed
- setScrubberVisible(canSeekMedia());
+ if (playbackState != null) {
+ updatePlaybackUi(playbackState);
} else {
- Log.d(TAG, "Controller missing data " + metadata + " " + playbackState);
clearTimer();
}
} else {
- Log.d(TAG, "No longer have a valid media controller");
clearTimer();
}
}
};
+ private void updatePlaybackUi(PlaybackState state) {
+ long position = state.getPosition();
+ mSeekBar.setProgress((int) position);
+
+ mSeekBarElapsedTime.setText(millisecondsToTimeString(position));
+
+ // Update scrubber in case available actions have changed
+ setScrubberVisible(canSeekMedia(state));
+ }
+
private String millisecondsToTimeString(long milliseconds) {
long seconds = milliseconds / 1000;
String text = DateUtils.formatElapsedTime(seconds);
@@ -295,6 +309,7 @@
int tintColor = getNotificationHeader().getOriginalIconColor();
mSeekBarElapsedTime.setTextColor(tintColor);
mSeekBarTotalTime.setTextColor(tintColor);
+ mSeekBarTotalTime.setShadowLayer(1.5f, 1.5f, 1.5f, mBackgroundColor);
ColorStateList tintList = ColorStateList.valueOf(tintColor);
mSeekBar.setThumbTintList(tintList);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index bbae62e..0b994ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -285,8 +285,6 @@
}
public void hide(boolean destroyView) {
- // TODO(b/113914868): investigation log for disappearing home button
- Log.i(TAG, "KeyguardBouncer.hide (b/113914868): destroyView=" + destroyView);
if (isShowing()) {
StatsLog.write(StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED,
StatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index c7f778d..3143971 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -90,11 +90,9 @@
private int mDensity;
private boolean mPulsing;
private boolean mDozing;
- private boolean mBouncerVisible;
private boolean mDocked;
private boolean mLastDozing;
private boolean mLastPulsing;
- private boolean mLastBouncerVisible;
private int mIconColor;
private float mDozeAmount;
private int mIconRes;
@@ -252,9 +250,9 @@
int state = getState();
mIsFaceUnlockState = state == STATE_SCANNING_FACE;
if (state != mLastState || mLastDozing != mDozing || mLastPulsing != mPulsing
- || mLastScreenOn != mScreenOn || mLastBouncerVisible != mBouncerVisible || force) {
+ || mLastScreenOn != mScreenOn || force) {
@LockAnimIndex final int lockAnimIndex = getAnimationIndexForTransition(mLastState,
- state, mLastPulsing, mPulsing, mLastDozing, mDozing, mBouncerVisible);
+ state, mLastPulsing, mPulsing, mLastDozing, mDozing);
boolean isAnim = lockAnimIndex != -1;
int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(state);
@@ -302,7 +300,6 @@
mLastScreenOn = mScreenOn;
mLastDozing = mDozing;
mLastPulsing = mPulsing;
- mLastBouncerVisible = mBouncerVisible;
}
boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked);
@@ -371,8 +368,7 @@
}
private int getAnimationIndexForTransition(int oldState, int newState,
- boolean wasPulsing, boolean pulsing, boolean wasDozing, boolean dozing,
- boolean bouncerVisible) {
+ boolean wasPulsing, boolean pulsing, boolean wasDozing, boolean dozing) {
// Never animate when screen is off
if (dozing && !pulsing && !mWasPulsingOnThisFrame) {
@@ -391,7 +387,7 @@
return UNLOCK;
} else if (justLocked) {
return LOCK;
- } else if (newState == STATE_SCANNING_FACE && bouncerVisible) {
+ } else if (newState == STATE_SCANNING_FACE) {
return SCANNING;
} else if ((nowPulsing || turningOn) && newState != STATE_LOCK_OPEN) {
return LOCK_IN;
@@ -496,17 +492,6 @@
setImageTintList(ColorStateList.valueOf(color));
}
- /**
- * If bouncer is visible or not.
- */
- public void setBouncerVisible(boolean bouncerVisible) {
- if (mBouncerVisible == bouncerVisible) {
- return;
- }
- mBouncerVisible = bouncerVisible;
- update();
- }
-
@Override
public void onDensityOrFontScaleChanged() {
ViewGroup.LayoutParams lp = getLayoutParams();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 8a895e1..3ec1609 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -597,10 +597,6 @@
boolean disableHome = isGesturalMode(mNavBarMode)
|| ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
- // TODO(b/113914868): investigation log for disappearing home button
- Log.i(TAG, "updateNavButtonIcons (b/113914868): home disabled=" + disableHome
- + " mDisabledFlags=" + mDisabledFlags);
-
// Always disable recents when alternate car mode UI is active and for secondary displays.
boolean disableRecent = isRecentsButtonDisabled();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index bb490f5..e8ca3ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -1,7 +1,5 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
-
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
@@ -17,7 +15,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ContrastColorUtil;
-import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
@@ -86,17 +83,6 @@
* Ratio representing being awake or in ambient mode, where 1 is dark and 0 awake.
*/
private float mDarkAmount;
- /**
- * Maximum translation to avoid burn in.
- */
- private int mBurnInOffset;
- /**
- * Height of the keyguard status bar (not the one after unlocking.)
- */
- private int mKeyguardStatusBarHeight;
-
- private final ViewClippingUtil.ClippingParameters mClippingParameters =
- view -> view instanceof StatusBarWindowView;
public NotificationIconAreaController(Context context, StatusBar statusBar,
StatusBarStateController statusBarStateController,
@@ -166,9 +152,6 @@
Resources res = context.getResources();
mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
mIconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_padding);
- mBurnInOffset = res.getDimensionPixelSize(R.dimen.default_burn_in_prevention_offset);
- mKeyguardStatusBarHeight = res
- .getDimensionPixelSize(R.dimen.status_bar_header_height_keyguard);
}
/**
@@ -479,49 +462,9 @@
mNotificationIcons.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate);
}
- /**
- * Moves icons whenever the device wakes up in AOD, to avoid burn in.
- */
- public void dozeTimeTick() {
- if (mNotificationIcons.getVisibility() != View.VISIBLE) {
- return;
- }
-
- if (mDarkAmount == 0 && !mStatusBarStateController.isDozing()) {
- mNotificationIcons.setTranslationX(0);
- mNotificationIcons.setTranslationY(0);
- mCenteredIcon.setTranslationX(0);
- mCenteredIcon.setTranslationY(0);
- return;
- }
-
- int yOffset = (mKeyguardStatusBarHeight - getHeight()) / 2;
- int translationX = getBurnInOffset(mBurnInOffset, true /* xAxis */);
- int translationY = getBurnInOffset(mBurnInOffset, false /* xAxis */) + yOffset;
- mNotificationIcons.setTranslationX(translationX);
- mNotificationIcons.setTranslationY(translationY);
- mCenteredIcon.setTranslationX(translationX);
- mCenteredIcon.setTranslationY(translationY);
- }
-
- @Override
- public void onDozingChanged(boolean isDozing) {
- dozeTimeTick();
- }
-
@Override
public void onDozeAmountChanged(float linear, float eased) {
- boolean wasOrIsAwake = mDarkAmount == 0 || linear == 0;
- boolean wasOrIsDozing = mDarkAmount == 1 || linear == 1;
mDarkAmount = linear;
- if (wasOrIsAwake) {
- ViewClippingUtil.setClippingDeactivated(mNotificationIcons, mDarkAmount != 0,
- mClippingParameters);
- }
- if (wasOrIsAwake || wasOrIsDozing) {
- dozeTimeTick();
- }
-
boolean fullyDark = mDarkAmount == 1f;
if (mFullyDark != fullyDark) {
mFullyDark = fullyDark;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index ee43879..17c200e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -42,10 +42,6 @@
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.UiOffloadThread;
-import com.android.systemui.privacy.PrivacyItem;
-import com.android.systemui.privacy.PrivacyItemController;
-import com.android.systemui.privacy.PrivacyItemControllerKt;
-import com.android.systemui.privacy.PrivacyType;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.statusbar.CommandQueue;
@@ -66,9 +62,6 @@
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.ZenModeController;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.List;
import java.util.Locale;
/**
@@ -83,12 +76,12 @@
ZenModeController.Callback,
DeviceProvisionedListener,
KeyguardMonitor.Callback,
- PrivacyItemController.Callback,
LocationController.LocationChangeCallback {
private static final String TAG = "PhoneStatusBarPolicy";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- public static final int LOCATION_STATUS_ICON_ID = PrivacyType.TYPE_LOCATION.getIconId();
+ public static final int LOCATION_STATUS_ICON_ID =
+ com.android.internal.R.drawable.perm_group_location;
private final String mSlotCast;
private final String mSlotHotspot;
@@ -102,8 +95,6 @@
private final String mSlotHeadset;
private final String mSlotDataSaver;
private final String mSlotLocation;
- private final String mSlotMicrophone;
- private final String mSlotCamera;
private final String mSlotSensorsOff;
private final Context mContext;
@@ -121,7 +112,6 @@
private final DeviceProvisionedController mProvisionedController;
private final KeyguardMonitor mKeyguardMonitor;
private final LocationController mLocationController;
- private final PrivacyItemController mPrivacyItemController;
private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
private final SensorPrivacyController mSensorPrivacyController;
@@ -154,7 +144,6 @@
mProvisionedController = Dependency.get(DeviceProvisionedController.class);
mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
mLocationController = Dependency.get(LocationController.class);
- mPrivacyItemController = Dependency.get(PrivacyItemController.class);
mSensorPrivacyController = Dependency.get(SensorPrivacyController.class);
mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
@@ -170,8 +159,6 @@
mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);
mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver);
mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location);
- mSlotMicrophone = context.getString(com.android.internal.R.string.status_bar_microphone);
- mSlotCamera = context.getString(com.android.internal.R.string.status_bar_camera);
mSlotSensorsOff = context.getString(com.android.internal.R.string.status_bar_sensors_off);
// listen for broadcasts
@@ -231,13 +218,6 @@
context.getString(R.string.accessibility_data_saver_on));
mIconController.setIconVisibility(mSlotDataSaver, false);
- // privacy items
- mIconController.setIcon(mSlotMicrophone, PrivacyType.TYPE_MICROPHONE.getIconId(),
- PrivacyType.TYPE_MICROPHONE.getName(mContext));
- mIconController.setIconVisibility(mSlotMicrophone, false);
- mIconController.setIcon(mSlotCamera, PrivacyType.TYPE_CAMERA.getIconId(),
- PrivacyType.TYPE_CAMERA.getName(mContext));
- mIconController.setIconVisibility(mSlotCamera, false);
mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
mContext.getString(R.string.accessibility_location_active));
mIconController.setIconVisibility(mSlotLocation, false);
@@ -257,7 +237,6 @@
mNextAlarmController.addCallback(mNextAlarmCallback);
mDataSaver.addCallback(this);
mKeyguardMonitor.addCallback(this);
- mPrivacyItemController.addCallback(this);
mSensorPrivacyController.addCallback(mSensorPrivacyListener);
mLocationController.addCallback(this);
@@ -601,46 +580,9 @@
mIconController.setIconVisibility(mSlotDataSaver, isDataSaving);
}
- @Override // PrivacyItemController.Callback
- public void privacyChanged(List<PrivacyItem> privacyItems) {
- updatePrivacyItems(privacyItems);
- }
-
- private void updatePrivacyItems(List<PrivacyItem> items) {
- boolean showCamera = false;
- boolean showMicrophone = false;
- boolean showLocation = false;
- for (PrivacyItem item : items) {
- if (item == null /* b/124234367 */) {
- if (DEBUG) {
- Log.e(TAG, "updatePrivacyItems - null item found");
- StringWriter out = new StringWriter();
- mPrivacyItemController.dump(null, new PrintWriter(out), null);
- Log.e(TAG, out.toString());
- }
- continue;
- }
- switch (item.getPrivacyType()) {
- case TYPE_CAMERA:
- showCamera = true;
- break;
- case TYPE_LOCATION:
- showLocation = true;
- break;
- case TYPE_MICROPHONE:
- showMicrophone = true;
- break;
- }
- }
-
- mIconController.setIconVisibility(mSlotCamera, showCamera);
- mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
- mIconController.setIconVisibility(mSlotLocation, showLocation);
- }
-
@Override
public void onLocationActiveChanged(boolean active) {
- if (!PrivacyItemControllerKt.isPermissionsHubEnabled()) updateLocation();
+ updateLocation();
}
// Updates the status view based on the current state of location requests.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index e60a30a..7f20d8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1314,7 +1314,6 @@
&& !mDozing
&& !ONLY_CORE_APPS;
mNotificationPanel.setQsExpansionEnabled(expandEnabled);
- // STOPSHIP(kozynski, b/129405675) Remove log
Log.d(TAG, "updateQsExpansionEnabled - QS Expand enabled: " + expandEnabled);
}
@@ -4020,7 +4019,6 @@
@Override
public void dozeTimeTick() {
mNotificationPanel.dozeTimeTick();
- mNotificationIconAreaController.dozeTimeTick();
if (mAmbientIndicationContainer instanceof DozeReceiver) {
((DozeReceiver) mAmbientIndicationContainer).dozeTimeTick();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index f726321..395add7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -111,7 +111,9 @@
public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor,
@Named(MAIN_HANDLER_NAME) Handler handler, ActivityStarter activityStarter) {
mContext = context;
- mGuestResumeSessionReceiver.register(context);
+ if (!UserManager.isGuestUserEphemeral()) {
+ mGuestResumeSessionReceiver.register(context);
+ }
mKeyguardMonitor = keyguardMonitor;
mHandler = handler;
mActivityStarter = activityStarter;
diff --git a/packages/SystemUI/src/com/android/systemui/util/AutoMarqueeTextView.java b/packages/SystemUI/src/com/android/systemui/util/AutoMarqueeTextView.java
index d9d410d..09dbfee 100644
--- a/packages/SystemUI/src/com/android/systemui/util/AutoMarqueeTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/util/AutoMarqueeTextView.java
@@ -49,6 +49,11 @@
}
@Override
+ protected void onFinishInflate() {
+ onVisibilityAggregated(isVisibleToUser());
+ }
+
+ @Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
setSelected(true);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
index 3330d1e..6891f56 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
@@ -20,12 +20,14 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
+import android.provider.DeviceConfig;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
@@ -50,6 +52,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
// Need to run tests on main looper because LiveData operations such as setData, observe,
@@ -63,7 +67,7 @@
private static final int SECONDARY_USER_ID = 11;
private static final Uri SETTINGS_URI = null;
- private ClockManager mClockManager;
+ ClockManager mClockManager;
private ContentObserver mContentObserver;
private DockManagerFake mFakeDockManager;
private MutableLiveData<Integer> mCurrentUser;
@@ -140,6 +144,33 @@
}
@Test
+ public void getCurrentClock_inBlackList() {
+ mClockManager = spy(mClockManager);
+ // GIVEN that settings is set to the bubble clock face
+ when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK);
+ // WHEN settings change event is fired
+ mContentObserver.onChange(false, SETTINGS_URI, MAIN_USER_ID);
+ // GIVEN that bubble clock is in blacklist
+ when(mClockManager.getBlackListFromConfig()).thenReturn(BUBBLE_CLOCK);
+ // WHEN device config change of systemui is fired
+ mClockManager.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
+ // THEN the result is null, indicated the current clock should be reset to the default one.
+ assertThat(mClockManager.getCurrentClock()).isNull();
+ }
+
+ @Test
+ public void getClockInfo_inBlackList() {
+ mClockManager = spy(mClockManager);
+ // GIVEN that bubble clock is in blacklist
+ when(mClockManager.getBlackListFromConfig()).thenReturn(BUBBLE_CLOCK);
+ // WHEN device config change of systemui is fired
+ mClockManager.onDeviceConfigPropertiesChanged(DeviceConfig.NAMESPACE_SYSTEMUI);
+ // THEN the ClockInfo should not contain bubble clock
+ List<ClockInfo> clocks = mClockManager.getClockInfos();
+ assertThat(clocks.stream().anyMatch(info -> BUBBLE_CLOCK.equals(info.getId()))).isFalse();
+ }
+
+ @Test
public void onClockChanged_customClock() {
// GIVEN that settings is set to the bubble clock face
when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index b9afea1..bd7f897 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -16,17 +16,18 @@
package com.android.systemui.appops;
+import static junit.framework.TestCase.assertFalse;
+
import static org.junit.Assert.assertEquals;
+import static org.junit.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.anyString;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.pm.PackageManager;
@@ -52,7 +53,6 @@
private static final String TEST_PACKAGE_NAME = "test";
private static final int TEST_UID = UserHandle.getUid(0, 0);
private static final int TEST_UID_OTHER = UserHandle.getUid(1, 0);
- private static final int TEST_UID_NON_USER_SENSITIVE = UserHandle.getUid(2, 0);
@Mock
private AppOpsManager mAppOpsManager;
@@ -71,18 +71,6 @@
getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager);
- // All permissions of TEST_UID and TEST_UID_OTHER are user sensitive. None of
- // TEST_UID_NON_USER_SENSITIVE are user sensitive.
- getContext().setMockPackageManager(mPackageManager);
- when(mPackageManager.getPermissionFlags(anyString(), anyString(),
- eq(UserHandle.getUserHandleForUid(TEST_UID)))).thenReturn(
- PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
- when(mPackageManager.getPermissionFlags(anyString(), anyString(),
- eq(UserHandle.getUserHandleForUid(TEST_UID_OTHER)))).thenReturn(
- PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
- when(mPackageManager.getPermissionFlags(anyString(), anyString(),
- eq(UserHandle.getUserHandleForUid(TEST_UID_NON_USER_SENSITIVE)))).thenReturn(0);
-
mController = new AppOpsControllerImpl(mContext, Dependency.get(Dependency.BG_LOOPER));
}
@@ -175,18 +163,27 @@
}
@Test
- public void nonUserSensitiveOpsAreIgnored() {
- mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
- TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true);
- assertEquals(0, mController.getActiveAppOpsForUser(
- UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size());
- }
-
- @Test
public void opNotedScheduledForRemoval() {
mController.setBGHandler(mMockHandler);
mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
AppOpsManager.MODE_ALLOWED);
verify(mMockHandler).scheduleRemoval(any(AppOpItem.class), anyLong());
}
+
+ @Test
+ public void noItemsAfterStopListening() {
+ mController.setBGHandler(mMockHandler);
+
+ mController.setListening(true);
+ mController.onOpActiveChanged(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+ true);
+ mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
+ AppOpsManager.MODE_ALLOWED);
+ assertFalse(mController.getActiveAppOps().isEmpty());
+
+ mController.setListening(false);
+
+ verify(mMockHandler).removeCallbacksAndMessages(null);
+ assertTrue(mController.getActiveAppOps().isEmpty());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
index 67df60a..9c2c822 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
@@ -36,8 +37,10 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
/**
* Tests color extraction generation.
@@ -54,62 +57,68 @@
ColorExtractor.TYPE_DARK,
ColorExtractor.TYPE_EXTRA_DARK};
+ private ColorExtractor.GradientColors mColors;
+ private SysuiColorExtractor mColorExtractor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mColors = new ColorExtractor.GradientColors();
+ mColors.setMainColor(Color.RED);
+ mColors.setSecondaryColor(Color.RED);
+ mColorExtractor = new SysuiColorExtractor(getContext(),
+ (inWallpaperColors, outGradientColorsNormal, outGradientColorsDark,
+ outGradientColorsExtraDark) -> {
+ outGradientColorsNormal.set(mColors);
+ outGradientColorsDark.set(mColors);
+ outGradientColorsExtraDark.set(mColors);
+ }, mock(ConfigurationController.class), false);
+ }
+
@Test
public void getColors_usesGreyIfWallpaperNotVisible() {
- ColorExtractor.GradientColors colors = new ColorExtractor.GradientColors();
- colors.setMainColor(Color.RED);
- colors.setSecondaryColor(Color.RED);
+ simulateEvent(mColorExtractor);
+ mColorExtractor.setWallpaperVisible(false);
- SysuiColorExtractor extractor = getTestableExtractor(colors);
- simulateEvent(extractor);
- extractor.setWallpaperVisible(false);
-
- ColorExtractor.GradientColors fallbackColors = extractor.getNeutralColors();
+ ColorExtractor.GradientColors fallbackColors = mColorExtractor.getNeutralColors();
for (int type : sTypes) {
assertEquals("Not using fallback!",
- extractor.getColors(WallpaperManager.FLAG_SYSTEM, type), fallbackColors);
+ mColorExtractor.getColors(WallpaperManager.FLAG_SYSTEM, type), fallbackColors);
assertNotEquals("Wallpaper visibility event should not affect lock wallpaper.",
- extractor.getColors(WallpaperManager.FLAG_LOCK, type), fallbackColors);
+ mColorExtractor.getColors(WallpaperManager.FLAG_LOCK, type), fallbackColors);
}
}
@Test
public void getColors_doesntUseFallbackIfVisible() {
- ColorExtractor.GradientColors colors = new ColorExtractor.GradientColors();
- colors.setMainColor(Color.RED);
- colors.setSecondaryColor(Color.RED);
+ mColors.setMainColor(Color.RED);
+ mColors.setSecondaryColor(Color.RED);
- SysuiColorExtractor extractor = getTestableExtractor(colors);
- simulateEvent(extractor);
- extractor.setWallpaperVisible(true);
+ simulateEvent(mColorExtractor);
+ mColorExtractor.setWallpaperVisible(true);
for (int which : sWhich) {
for (int type : sTypes) {
assertEquals("Not using extracted colors!",
- extractor.getColors(which, type), colors);
+ mColorExtractor.getColors(which, type), mColors);
}
}
}
@Test
public void getColors_fallbackWhenMediaIsVisible() {
- ColorExtractor.GradientColors colors = new ColorExtractor.GradientColors();
- colors.setMainColor(Color.RED);
- colors.setSecondaryColor(Color.RED);
+ simulateEvent(mColorExtractor);
+ mColorExtractor.setWallpaperVisible(true);
+ mColorExtractor.setHasBackdrop(true);
- SysuiColorExtractor extractor = getTestableExtractor(colors);
- simulateEvent(extractor);
- extractor.setWallpaperVisible(true);
- extractor.setHasBackdrop(true);
-
- ColorExtractor.GradientColors fallbackColors = extractor.getNeutralColors();
+ ColorExtractor.GradientColors fallbackColors = mColorExtractor.getNeutralColors();
for (int type : sTypes) {
assertEquals("Not using fallback!",
- extractor.getColors(WallpaperManager.FLAG_LOCK, type), fallbackColors);
+ mColorExtractor.getColors(WallpaperManager.FLAG_LOCK, type), fallbackColors);
assertNotEquals("Media visibility should not affect system wallpaper.",
- extractor.getColors(WallpaperManager.FLAG_SYSTEM, type), fallbackColors);
+ mColorExtractor.getColors(WallpaperManager.FLAG_SYSTEM, type), fallbackColors);
}
}
@@ -126,14 +135,13 @@
verify(tonal).applyFallback(any(), any());
}
- private SysuiColorExtractor getTestableExtractor(ColorExtractor.GradientColors colors) {
- return new SysuiColorExtractor(getContext(),
- (inWallpaperColors, outGradientColorsNormal, outGradientColorsDark,
- outGradientColorsExtraDark) -> {
- outGradientColorsNormal.set(colors);
- outGradientColorsDark.set(colors);
- outGradientColorsExtraDark.set(colors);
- }, mock(ConfigurationController.class), false);
+ @Test
+ public void onUiModeChanged_notifiesListener() {
+ ColorExtractor.OnColorsChangedListener listener = mock(
+ ColorExtractor.OnColorsChangedListener.class);
+ mColorExtractor.addOnColorsChangedListener(listener);
+ mColorExtractor.onUiModeChanged();
+ verify(listener).onColorsChanged(any(), anyInt());
}
private void simulateEvent(SysuiColorExtractor extractor) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
deleted file mode 100644
index 6302f9d..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogBuilderTest.kt
+++ /dev/null
@@ -1,77 +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.systemui.privacy
-
-import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
-import com.android.systemui.SysuiTestCase
-import org.junit.Assert.assertEquals
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class PrivacyDialogBuilderTest : SysuiTestCase() {
-
- companion object {
- val TEST_UID = 1
- }
-
- @Test
- fun testGenerateAppsList() {
- val bar2 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
- "Bar", TEST_UID, context))
- val bar3 = PrivacyItem(Privacy.TYPE_LOCATION, PrivacyApplication(
- "Bar", TEST_UID, context))
- val foo0 = PrivacyItem(Privacy.TYPE_MICROPHONE, PrivacyApplication(
- "Foo", TEST_UID, context))
- val baz1 = PrivacyItem(Privacy.TYPE_CAMERA, PrivacyApplication(
- "Baz", TEST_UID, context))
-
- val items = listOf(bar2, foo0, baz1, bar3)
-
- val textBuilder = PrivacyDialogBuilder(context, items)
-
- val list = textBuilder.appsAndTypes
- assertEquals(3, list.size)
- val appsList = list.map { it.first }
- val typesList = list.map { it.second }
- // List is sorted by number of types and then by types
- assertEquals(listOf("Bar", "Baz", "Foo"), appsList.map { it.packageName })
- assertEquals(listOf(Privacy.TYPE_CAMERA, Privacy.TYPE_LOCATION), typesList[0])
- assertEquals(listOf(Privacy.TYPE_CAMERA), typesList[1])
- assertEquals(listOf(Privacy.TYPE_MICROPHONE), typesList[2])
- }
-
- @Test
- fun testOrder() {
- // We want location to always go last, so it will go in the "+ other apps"
- val appCamera = PrivacyItem(PrivacyType.TYPE_CAMERA,
- PrivacyApplication("Camera", TEST_UID, context))
- val appMicrophone =
- PrivacyItem(PrivacyType.TYPE_MICROPHONE,
- PrivacyApplication("Microphone", TEST_UID, context))
- val appLocation =
- PrivacyItem(PrivacyType.TYPE_LOCATION,
- PrivacyApplication("Location", TEST_UID, context))
-
- val items = listOf(appLocation, appMicrophone, appCamera)
- val textBuilder = PrivacyDialogBuilder(context, items)
- val appList = textBuilder.appsAndTypes.map { it.first }.map { it.packageName }
- assertEquals(listOf("Camera", "Microphone", "Location"), appList)
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
deleted file mode 100644
index e2e0bb1..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ /dev/null
@@ -1,293 +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.systemui.privacy
-
-import android.app.ActivityManager
-import android.app.AppOpsManager
-import android.content.Context
-import android.content.Intent
-import android.content.pm.UserInfo
-import android.os.Handler
-import android.os.UserHandle
-import android.os.UserManager
-import android.provider.DeviceConfig
-import android.provider.Settings.RESET_MODE_PACKAGE_DEFAULTS
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import android.testing.TestableLooper.RunWithLooper
-import androidx.test.filters.SmallTest
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
-import com.android.systemui.Dependency
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.appops.AppOpItem
-import com.android.systemui.appops.AppOpsController
-import org.hamcrest.Matchers.hasItem
-import org.hamcrest.Matchers.not
-import org.hamcrest.Matchers.nullValue
-import org.junit.After
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertThat
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.anyList
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.atLeastOnce
-import org.mockito.Mockito.doReturn
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.spy
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.MockitoAnnotations
-
-@RunWith(AndroidTestingRunner::class)
-@SmallTest
-@RunWithLooper
-class PrivacyItemControllerTest : SysuiTestCase() {
-
- companion object {
- val CURRENT_USER_ID = ActivityManager.getCurrentUser()
- val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE
- const val SYSTEM_UID = 1000
- const val TEST_PACKAGE_NAME = "test"
- const val DEVICE_SERVICES_STRING = "Device services"
- const val TAG = "PrivacyItemControllerTest"
- fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
- }
-
- @Mock
- private lateinit var appOpsController: AppOpsController
- @Mock
- private lateinit var callback: PrivacyItemController.Callback
- @Mock
- private lateinit var userManager: UserManager
- @Captor
- private lateinit var argCaptor: ArgumentCaptor<List<PrivacyItem>>
- @Captor
- private lateinit var argCaptorCallback: ArgumentCaptor<AppOpsController.Callback>
-
- private lateinit var testableLooper: TestableLooper
- private lateinit var privacyItemController: PrivacyItemController
- private lateinit var handler: Handler
-
- fun PrivacyItemController(context: Context) =
- PrivacyItemController(context, appOpsController, handler, handler)
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- testableLooper = TestableLooper.get(this)
- handler = Handler(testableLooper.looper)
-
- appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
- mDependency.injectTestDependency(Dependency.BG_HANDLER, handler)
- mDependency.injectTestDependency(Dependency.MAIN_HANDLER, handler)
- mContext.addMockSystemService(UserManager::class.java, userManager)
- mContext.getOrCreateTestableResources().addOverride(R.string.device_services,
- DEVICE_SERVICES_STRING)
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
- SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
- "true", false)
-
- doReturn(listOf(object : UserInfo() {
- init {
- id = CURRENT_USER_ID
- }
- })).`when`(userManager).getProfiles(anyInt())
-
- privacyItemController = PrivacyItemController(mContext)
- }
-
- @After
- fun tearDown() {
- DeviceConfig.resetToDefaults(RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_PRIVACY)
- }
-
- @Test
- fun testSetListeningTrueByAddingCallback() {
- privacyItemController.addCallback(callback)
- testableLooper.processAllMessages()
- verify(appOpsController).addCallback(eq(PrivacyItemController.OPS),
- any(AppOpsController.Callback::class.java))
- testableLooper.processAllMessages()
- verify(callback).privacyChanged(anyList())
- }
-
- @Test
- fun testSetListeningFalseByRemovingLastCallback() {
- privacyItemController.addCallback(callback)
- testableLooper.processAllMessages()
- verify(appOpsController, never()).removeCallback(any(IntArray::class.java),
- any(AppOpsController.Callback::class.java))
- privacyItemController.removeCallback(callback)
- testableLooper.processAllMessages()
- verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS),
- any(AppOpsController.Callback::class.java))
- verify(callback).privacyChanged(emptyList())
- }
-
- @Test
- fun testDistinctItems() {
- doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
- AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 1)))
- .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
-
- privacyItemController.addCallback(callback)
- testableLooper.processAllMessages()
- verify(callback).privacyChanged(capture(argCaptor))
- assertEquals(1, argCaptor.value.size)
- }
-
- @Test
- fun testSystemApps() {
- doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, SYSTEM_UID, TEST_PACKAGE_NAME,
- 0))).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
- privacyItemController.addCallback(callback)
- testableLooper.processAllMessages()
- verify(callback).privacyChanged(capture(argCaptor))
- assertEquals(1, argCaptor.value.size)
- assertEquals(context.getString(R.string.device_services),
- argCaptor.value[0].application.applicationName)
- }
-
- @Test
- fun testRegisterReceiver_allUsers() {
- val spiedContext = spy(mContext)
- val itemController = PrivacyItemController(spiedContext)
- itemController.addCallback(callback)
- testableLooper.processAllMessages()
- verify(spiedContext, atLeastOnce()).registerReceiverAsUser(
- eq(itemController.userSwitcherReceiver), eq(UserHandle.ALL), any(), eq(null),
- eq(null))
- verify(spiedContext, never()).unregisterReceiver(eq(itemController.userSwitcherReceiver))
- }
-
- @Test
- fun testReceiver_ACTION_USER_FOREGROUND() {
- privacyItemController.userSwitcherReceiver.onReceive(context,
- Intent(Intent.ACTION_USER_FOREGROUND))
- verify(userManager).getProfiles(anyInt())
- }
-
- @Test
- fun testReceiver_ACTION_MANAGED_PROFILE_ADDED() {
- privacyItemController.userSwitcherReceiver.onReceive(context,
- Intent(Intent.ACTION_MANAGED_PROFILE_ADDED))
- verify(userManager).getProfiles(anyInt())
- }
-
- @Test
- fun testReceiver_ACTION_MANAGED_PROFILE_REMOVED() {
- privacyItemController.userSwitcherReceiver.onReceive(context,
- Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED))
- verify(userManager).getProfiles(anyInt())
- }
-
- @Test
- fun testAddMultipleCallbacks() {
- val otherCallback = mock(PrivacyItemController.Callback::class.java)
- privacyItemController.addCallback(callback)
- testableLooper.processAllMessages()
- verify(callback).privacyChanged(anyList())
-
- privacyItemController.addCallback(otherCallback)
- testableLooper.processAllMessages()
- verify(otherCallback).privacyChanged(anyList())
- // Adding a callback should not unnecessarily call previous ones
- verifyNoMoreInteractions(callback)
- }
-
- @Test
- fun testMultipleCallbacksAreUpdated() {
- doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
-
- val otherCallback = mock(PrivacyItemController.Callback::class.java)
- privacyItemController.addCallback(callback)
- privacyItemController.addCallback(otherCallback)
- testableLooper.processAllMessages()
- reset(callback)
- reset(otherCallback)
-
- verify(appOpsController).addCallback(any<IntArray>(), capture(argCaptorCallback))
- argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
- testableLooper.processAllMessages()
- verify(callback).privacyChanged(anyList())
- verify(otherCallback).privacyChanged(anyList())
- }
-
- @Test
- fun testRemoveCallback() {
- doReturn(emptyList<AppOpItem>()).`when`(appOpsController).getActiveAppOpsForUser(anyInt())
- val otherCallback = mock(PrivacyItemController.Callback::class.java)
- privacyItemController.addCallback(callback)
- privacyItemController.addCallback(otherCallback)
- testableLooper.processAllMessages()
- reset(callback)
- reset(otherCallback)
-
- verify(appOpsController).addCallback(any<IntArray>(), capture(argCaptorCallback))
- privacyItemController.removeCallback(callback)
- argCaptorCallback.value.onActiveStateChanged(0, TEST_UID, "", true)
- testableLooper.processAllMessages()
- verify(callback, never()).privacyChanged(anyList())
- verify(otherCallback).privacyChanged(anyList())
- }
-
- @Test
- fun testListShouldNotHaveNull() {
- doReturn(listOf(AppOpItem(AppOpsManager.OP_ACTIVATE_VPN, TEST_UID, "", 0),
- AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
- .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
- privacyItemController.addCallback(callback)
- testableLooper.processAllMessages()
-
- verify(callback).privacyChanged(capture(argCaptor))
- assertEquals(1, argCaptor.value.size)
- assertThat(argCaptor.value, not(hasItem(nullValue())))
- }
-
- @Test
- fun testListShouldBeCopy() {
- val list = listOf(PrivacyItem(PrivacyType.TYPE_CAMERA,
- PrivacyApplication("", TEST_UID, mContext)))
- privacyItemController.privacyList = list
- val privacyList = privacyItemController.privacyList
- assertEquals(list, privacyList)
- assertTrue(list !== privacyList)
- }
-
- @Test
- fun testNotListeningWhenIndicatorsDisabled() {
- privacyItemController.devicePropertyChangedListener.onPropertyChanged(
- DeviceConfig.NAMESPACE_PRIVACY,
- SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
- "false")
- privacyItemController.addCallback(callback)
- testableLooper.processAllMessages()
- verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS),
- any(AppOpsController.Callback::class.java))
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
index b35dcb6..dd2630b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
@@ -16,17 +16,20 @@
package com.android.systemui.statusbar.notification;
-import static junit.framework.Assert.assertEquals;
-
-import static org.mockito.Matchers.anyObject;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.os.Handler;
import android.service.notification.StatusBarNotification;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationPresenter;
@@ -38,11 +41,13 @@
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper()
public class VisualStabilityManagerTest extends SysuiTestCase {
- private VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager(
- mock(NotificationEntryManager.class));
+ private TestableLooper mTestableLooper;
+
+ private VisualStabilityManager mVisualStabilityManager;
private VisualStabilityManager.Callback mCallback = mock(VisualStabilityManager.Callback.class);
private VisibilityLocationProvider mLocationProvider = mock(VisibilityLocationProvider.class);
private ExpandableNotificationRow mRow = mock(ExpandableNotificationRow.class);
@@ -50,46 +55,53 @@
@Before
public void setUp() {
+ mTestableLooper = TestableLooper.get(this);
+ mVisualStabilityManager = new VisualStabilityManager(
+ mock(NotificationEntryManager.class),
+ new Handler(mTestableLooper.getLooper()));
+
mVisualStabilityManager.setUpWithPresenter(mock(NotificationPresenter.class));
mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider);
mEntry = new NotificationEntry(mock(StatusBarNotification.class));
mEntry.setRow(mRow);
+
+ when(mRow.getEntry()).thenReturn(mEntry);
}
@Test
public void testPanelExpansion() {
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false);
+ assertFalse(mVisualStabilityManager.canReorderNotification(mRow));
mVisualStabilityManager.setPanelExpanded(false);
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true);
+ assertTrue(mVisualStabilityManager.canReorderNotification(mRow));
}
@Test
public void testScreenOn() {
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false);
+ assertFalse(mVisualStabilityManager.canReorderNotification(mRow));
mVisualStabilityManager.setScreenOn(false);
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true);
+ assertTrue(mVisualStabilityManager.canReorderNotification(mRow));
}
@Test
public void testReorderingAllowedChangesScreenOn() {
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
- assertEquals(mVisualStabilityManager.isReorderingAllowed(), false);
+ assertFalse(mVisualStabilityManager.isReorderingAllowed());
mVisualStabilityManager.setScreenOn(false);
- assertEquals(mVisualStabilityManager.isReorderingAllowed(), true);
+ assertTrue(mVisualStabilityManager.isReorderingAllowed());
}
@Test
public void testReorderingAllowedChangesPanel() {
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
- assertEquals(mVisualStabilityManager.isReorderingAllowed(), false);
+ assertFalse(mVisualStabilityManager.isReorderingAllowed());
mVisualStabilityManager.setPanelExpanded(false);
- assertEquals(mVisualStabilityManager.isReorderingAllowed(), true);
+ assertTrue(mVisualStabilityManager.isReorderingAllowed());
}
@Test
@@ -126,51 +138,51 @@
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
mVisualStabilityManager.notifyViewAddition(mRow);
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true);
+ assertTrue(mVisualStabilityManager.canReorderNotification(mRow));
}
@Test
public void testReorderingVisibleHeadsUpNotAllowed() {
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
- when(mLocationProvider.isInVisibleLocation(anyObject())).thenReturn(true);
+ when(mLocationProvider.isInVisibleLocation(any(NotificationEntry.class))).thenReturn(true);
mVisualStabilityManager.onHeadsUpStateChanged(mEntry, true);
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false);
+ assertFalse(mVisualStabilityManager.canReorderNotification(mRow));
}
@Test
public void testReorderingVisibleHeadsUpAllowed() {
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
- when(mLocationProvider.isInVisibleLocation(anyObject())).thenReturn(false);
+ when(mLocationProvider.isInVisibleLocation(any(NotificationEntry.class))).thenReturn(false);
mVisualStabilityManager.onHeadsUpStateChanged(mEntry, true);
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true);
+ assertTrue(mVisualStabilityManager.canReorderNotification(mRow));
}
@Test
public void testReorderingVisibleHeadsUpAllowedOnce() {
mVisualStabilityManager.setPanelExpanded(true);
mVisualStabilityManager.setScreenOn(true);
- when(mLocationProvider.isInVisibleLocation(anyObject())).thenReturn(false);
+ when(mLocationProvider.isInVisibleLocation(any(NotificationEntry.class))).thenReturn(false);
mVisualStabilityManager.onHeadsUpStateChanged(mEntry, true);
mVisualStabilityManager.onReorderingFinished();
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false);
+ assertFalse(mVisualStabilityManager.canReorderNotification(mRow));
}
@Test
public void testPulsing() {
mVisualStabilityManager.setPulsing(true);
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false);
+ assertFalse(mVisualStabilityManager.canReorderNotification(mRow));
mVisualStabilityManager.setPulsing(false);
- assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true);
+ assertTrue(mVisualStabilityManager.canReorderNotification(mRow));
}
@Test
public void testReorderingAllowedChanges_Pulsing() {
mVisualStabilityManager.setPulsing(true);
- assertEquals(mVisualStabilityManager.isReorderingAllowed(), false);
+ assertFalse(mVisualStabilityManager.isReorderingAllowed());
mVisualStabilityManager.setPulsing(false);
- assertEquals(mVisualStabilityManager.isReorderingAllowed(), true);
+ assertTrue(mVisualStabilityManager.isReorderingAllowed());
}
@Test
@@ -180,4 +192,49 @@
mVisualStabilityManager.setPulsing(false);
verify(mCallback).onReorderingAllowed();
}
+
+ @Test
+ public void testTemporarilyAllowReorderingNotifiesCallbacks() {
+ // GIVEN having the panel open (which would block reordering)
+ mVisualStabilityManager.setScreenOn(true);
+ mVisualStabilityManager.setPanelExpanded(true);
+ mVisualStabilityManager.addReorderingAllowedCallback(mCallback);
+
+ // WHEN we temprarily allow reordering
+ mVisualStabilityManager.temporarilyAllowReordering();
+
+ // THEN callbacks are notified that reordering is allowed
+ verify(mCallback).onReorderingAllowed();
+ assertTrue(mVisualStabilityManager.isReorderingAllowed());
+ }
+
+ @Test
+ public void testTemporarilyAllowReorderingDoesntOverridePulsing() {
+ // GIVEN we are in a pulsing state
+ mVisualStabilityManager.setPulsing(true);
+ mVisualStabilityManager.addReorderingAllowedCallback(mCallback);
+
+ // WHEN we temprarily allow reordering
+ mVisualStabilityManager.temporarilyAllowReordering();
+
+ // THEN reordering is still not allowed
+ verify(mCallback, never()).onReorderingAllowed();
+ assertFalse(mVisualStabilityManager.isReorderingAllowed());
+ }
+
+ @Test
+ public void testTemporarilyAllowReorderingExpires() {
+ // GIVEN having the panel open (which would block reordering)
+ mVisualStabilityManager.setScreenOn(true);
+ mVisualStabilityManager.setPanelExpanded(true);
+ mVisualStabilityManager.addReorderingAllowedCallback(mCallback);
+
+ // WHEN we temprarily allow reordering and then wait until the window expires
+ mVisualStabilityManager.temporarilyAllowReordering();
+ assertTrue(mVisualStabilityManager.isReorderingAllowed());
+ mTestableLooper.processMessages(1);
+
+ // THEN reordering is no longer allowed
+ assertFalse(mVisualStabilityManager.isReorderingAllowed());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 6376bd3..ef13b61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -63,6 +63,7 @@
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
@@ -97,6 +98,7 @@
@Rule public MockitoRule mockito = MockitoJUnit.rule();
@Mock private MetricsLogger mMetricsLogger;
+ @Mock private VisualStabilityManager mVisualStabilityManager;
@Mock private NotificationPresenter mPresenter;
@Mock private NotificationActivityStarter mNotificationActivityStarter;
@Mock private NotificationStackScrollLayout mStackScroller;
@@ -111,11 +113,12 @@
mDependency.injectTestDependency(DeviceProvisionedController.class,
mDeviceProvisionedController);
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
+ mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
mHandler = Handler.createAsync(mTestableLooper.getLooper());
mHelper = new NotificationTestHelper(mContext);
- mGutsManager = new NotificationGutsManager(mContext);
+ mGutsManager = new NotificationGutsManager(mContext, mVisualStabilityManager);
mGutsManager.setUpWithPresenter(mPresenter, mStackScroller,
mCheckSaveListener, mOnSettingsClickListener);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
@@ -316,6 +319,7 @@
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
any(INotificationManager.class),
+ eq(mVisualStabilityManager),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
anySet(),
@@ -344,6 +348,7 @@
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
any(INotificationManager.class),
+ eq(mVisualStabilityManager),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
anySet(),
@@ -374,6 +379,7 @@
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
any(INotificationManager.class),
+ eq(mVisualStabilityManager),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
anySet(),
@@ -403,6 +409,7 @@
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
any(INotificationManager.class),
+ eq(mVisualStabilityManager),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
anySet(),
@@ -431,6 +438,7 @@
verify(notificationInfoView).bindNotification(
any(PackageManager.class),
any(INotificationManager.class),
+ eq(mVisualStabilityManager),
eq(statusBarNotification.getPackageName()),
any(NotificationChannel.class),
anySet(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 06acc73..703adf7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -72,6 +72,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
import org.junit.After;
import org.junit.Before;
@@ -116,6 +117,8 @@
private PackageManager mMockPackageManager;
@Mock
private NotificationBlockingHelperManager mBlockingHelperManager;
+ @Mock
+ private VisualStabilityManager mVisualStabilityManager;
@Before
public void setUp() throws Exception {
@@ -193,11 +196,21 @@
@Test
public void testBindNotification_SetsTextApplicationName() throws Exception {
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
- null, null, null,
- true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView textView = mNotificationInfo.findViewById(R.id.pkgname);
assertTrue(textView.getText().toString().contains("App Name"));
assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -208,20 +221,42 @@
final Drawable iconDrawable = mock(Drawable.class);
when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
.thenReturn(iconDrawable);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
- null, null, null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon);
assertEquals(iconDrawable, iconView.getDrawable());
}
@Test
public void testBindNotification_noDelegate() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
- null, null, null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(GONE, nameView.getVisibility());
final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
@@ -238,10 +273,21 @@
applicationInfo);
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("Other");
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
assertEquals(VISIBLE, nameView.getVisibility());
assertTrue(nameView.getText().toString().contains("Proxied"));
@@ -251,10 +297,21 @@
@Test
public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(GONE, groupNameView.getVisibility());
}
@@ -267,10 +324,21 @@
when(mMockINotificationManager.getNotificationChannelGroupForPackage(
eq("test_group_id"), eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
.thenReturn(notificationChannelGroup);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
assertEquals(View.VISIBLE, groupNameView.getVisibility());
assertEquals("Test Group Name", groupNameView.getText());
@@ -278,19 +346,42 @@
@Test
public void testBindNotification_SetsTextChannelName() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(TEST_CHANNEL_NAME, textView.getText());
}
@Test
public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mDefaultNotificationChannel, mDefaultNotificationChannelSet,
- mSbn, null, null, null, true, false, IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mDefaultNotificationChannel,
+ mDefaultNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, textView.getVisibility());
}
@@ -301,30 +392,64 @@
// Package has one channel by default.
when(mMockINotificationManager.getNumNotificationChannelsForPackage(
eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mDefaultNotificationChannel, mDefaultNotificationChannelSet,
- mSbn, null, null, null, true,
- false, IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mDefaultNotificationChannel,
+ mDefaultNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@Test
public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, true,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ true,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(VISIBLE, textView.getVisibility());
}
@Test
public void testBindNotification_BlockLink_BlockingHelper() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, mock(
- NotificationInfo.OnSettingsClickListener.class), null, true, false,
- true /* isBlockingHelper */, IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ mock(NotificationInfo.OnSettingsClickListener.class),
+ null,
+ true,
+ false,
+ true /* isBlockingHelper */,
+ IMPORTANCE_DEFAULT,
+ true);
final View block =
mNotificationInfo.findViewById(R.id.blocking_helper_turn_off_notifications);
final View interruptivenessSettings = mNotificationInfo.findViewById(
@@ -336,12 +461,24 @@
@Test
public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
latch.countDown();
- }, null, true, false, IMPORTANCE_DEFAULT, true);
+ },
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
settingsButton.performClick();
@@ -351,10 +488,21 @@
@Test
public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@@ -362,36 +510,80 @@
@Test
public void testBindNotification_SettingsButtonInvisibleWhenDeviceUnprovisioned()
throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
- }, null, false, false, IMPORTANCE_DEFAULT, true);
+ },
+ null,
+ false,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertTrue(settingsButton.getVisibility() != View.VISIBLE);
}
@Test
public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null,
- (View v, NotificationChannel c, int appUid) -> {
- }, null, true, false, IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ (View v, NotificationChannel c, int appUid) -> { },
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final View settingsButton = mNotificationInfo.findViewById(R.id.info);
assertEquals(View.VISIBLE, settingsButton.getVisibility());
}
@Test
public void testBindNotificationLogging_notBlockingHelper() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
- null, null, null,
- true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
verify(mMetricsLogger).write(argThat(logMaker ->
logMaker.getCategory() == MetricsEvent.ACTION_NOTE_CONTROLS
&& logMaker.getType() == MetricsEvent.TYPE_OPEN
@@ -401,12 +593,22 @@
@Test
public void testBindNotificationLogging_BlockingHelper() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
- null, null, null,
- false, true,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ false,
true,
- IMPORTANCE_DEFAULT, true);
+ true,
+ IMPORTANCE_DEFAULT,
+ true);
verify(mMetricsLogger).write(argThat(logMaker ->
logMaker.getCategory() == MetricsEvent.ACTION_NOTE_CONTROLS
&& logMaker.getType() == MetricsEvent.TYPE_OPEN
@@ -416,12 +618,22 @@
@Test
public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
- null, null, null,
- false, true,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ false,
true,
- IMPORTANCE_DEFAULT, true);
+ true,
+ IMPORTANCE_DEFAULT,
+ true);
mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent");
verify(mMetricsLogger).count(eq("HowCanNotifsBeRealIfAppsArent"), eq(1));
}
@@ -429,13 +641,23 @@
@Test
public void testOnClickListenerPassesNullChannelForBundle() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
TEST_PACKAGE_NAME, mNotificationChannel,
- createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), mSbn, null,
+ createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
+ mSbn,
+ null,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(null, c);
latch.countDown();
- }, null, true, true, IMPORTANCE_DEFAULT, true);
+ },
+ null,
+ true,
+ true,
+ IMPORTANCE_DEFAULT,
+ true);
mNotificationInfo.findViewById(R.id.info).performClick();
// Verify that listener was triggered.
@@ -446,10 +668,21 @@
@UiThreadTest
public void testBindNotification_ChannelNameInvisibleWhenBundleFromDifferentChannels()
throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel,
- createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), mSbn, null, null,
- null, true, false, IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView channelNameView =
mNotificationInfo.findViewById(R.id.channel_name);
assertEquals(GONE, channelNameView.getVisibility());
@@ -458,10 +691,21 @@
@Test
@UiThreadTest
public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel,
- createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT), mSbn, null, null,
- null, true, false, IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ createMultipleChannelSet(MULTIPLE_CHANNEL_COUNT),
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
assertEquals(GONE, mNotificationInfo.findViewById(
R.id.interruptiveness_settings).getVisibility());
assertEquals(VISIBLE, mNotificationInfo.findViewById(
@@ -470,10 +714,21 @@
@Test
public void testBindNotification_whenAppUnblockable() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, true,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ true,
+ IMPORTANCE_DEFAULT,
+ true);
final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_text);
assertEquals(View.VISIBLE, view.getVisibility());
assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -484,10 +739,21 @@
@Test
public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
mTestableLooper.processAllMessages();
verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
anyString(), eq(TEST_UID), any());
@@ -496,10 +762,21 @@
@Test
public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_LOW, false);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_LOW,
+ false);
mNotificationInfo.findViewById(R.id.alert).performClick();
mTestableLooper.processAllMessages();
@@ -511,10 +788,21 @@
public void testDoesNotUpdateNotificationChannelAfterImportanceChangedSilenced()
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
mNotificationInfo.findViewById(R.id.silence).performClick();
mTestableLooper.processAllMessages();
@@ -526,10 +814,21 @@
public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnchanged()
throws Exception {
int originalImportance = mNotificationChannel.getImportance();
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
mNotificationInfo.handleCloseControls(true, false);
mTestableLooper.processAllMessages();
@@ -542,10 +841,21 @@
public void testHandleCloseControls_DoesNotUpdateNotificationChannelIfUnspecified()
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_UNSPECIFIED, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_UNSPECIFIED,
+ true);
mNotificationInfo.handleCloseControls(true, false);
@@ -561,13 +871,22 @@
NotificationInfo.CheckSaveListener listener =
mock(NotificationInfo.CheckSaveListener.class);
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
- createMultipleChannelSet(10) /* numUniqueChannelsInRow */, mSbn,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel /* notificationChannel */,
+ createMultipleChannelSet(10) /* numUniqueChannelsInRow */,
+ mSbn,
listener /* checkSaveListener */,
- null /* onSettingsClick */, null /* onAppSettingsClick */, true /* provisioned */,
- false /* isNonblockable */, true /* isForBlockingHelper */,
- IMPORTANCE_DEFAULT, true);
+ null /* onSettingsClick */,
+ null /* onAppSettingsClick */,
+ true /* provisioned */,
+ false /* isNonblockable */,
+ true /* isForBlockingHelper */,
+ IMPORTANCE_DEFAULT,
+ true);
NotificationGuts guts = spy(new NotificationGuts(mContext, null));
when(guts.getWindowToken()).thenReturn(mock(IBinder.class));
@@ -591,13 +910,21 @@
NotificationInfo.CheckSaveListener listener =
mock(NotificationInfo.CheckSaveListener.class);
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
- createMultipleChannelSet(10) /* numUniqueChannelsInRow */, mSbn,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel /* notificationChannel */,
+ createMultipleChannelSet(10) /* numUniqueChannelsInRow */,
+ mSbn,
listener /* checkSaveListener */,
- null /* onSettingsClick */, null /* onAppSettingsClick */,
- false /* isNonblockable */, true /* isForBlockingHelper */,
- true, IMPORTANCE_DEFAULT, true);
+ null /* onSettingsClick */,
+ null /* onAppSettingsClick */,
+ false /* isNonblockable */,
+ true /* isForBlockingHelper */,
+ true, IMPORTANCE_DEFAULT,
+ true);
mNotificationInfo.handleCloseControls(true /* save */, false /* force */);
@@ -611,14 +938,22 @@
NotificationInfo.CheckSaveListener listener =
mock(NotificationInfo.CheckSaveListener.class);
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */,
- createMultipleChannelSet(10) /* numUniqueChannelsInRow */, mSbn,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel /* notificationChannel */,
+ createMultipleChannelSet(10) /* numUniqueChannelsInRow */,
+ mSbn,
listener /* checkSaveListener */,
- null /* onSettingsClick */, null /* onAppSettingsClick */,
+ null /* onSettingsClick */,
+ null /* onAppSettingsClick */,
true /* provisioned */,
- false /* isNonblockable */, true /* isForBlockingHelper */,
- IMPORTANCE_DEFAULT, true);
+ false /* isNonblockable */,
+ true /* isForBlockingHelper */,
+ IMPORTANCE_DEFAULT,
+ true);
mNotificationInfo.findViewById(R.id.deliver_silently).performClick();
mTestableLooper.processAllMessages();
@@ -631,6 +966,7 @@
mNotificationInfo.bindNotification(
mMockPackageManager,
mMockINotificationManager,
+ mVisualStabilityManager,
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet /* numChannels */,
@@ -641,12 +977,13 @@
false /* isNonblockable */,
true /* isForBlockingHelper */,
true,
- IMPORTANCE_DEFAULT, true);
+ IMPORTANCE_DEFAULT,
+ true);
NotificationGuts guts = mock(NotificationGuts.class);
doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean());
mNotificationInfo.setGutsParent(guts);
- mNotificationInfo.closeControls(mNotificationInfo);
+ mNotificationInfo.closeControls(mNotificationInfo, true);
verify(mBlockingHelperManager).dismissCurrentBlockingHelper();
}
@@ -658,6 +995,7 @@
mNotificationInfo.bindNotification(
mMockPackageManager,
mMockINotificationManager,
+ mVisualStabilityManager,
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet /* numChannels */,
@@ -688,10 +1026,21 @@
@Test
public void testKeepUpdatesNotificationChannel_blockingHelper() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, true,
- IMPORTANCE_LOW, false);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ true,
+ IMPORTANCE_LOW,
+ false);
mNotificationInfo.findViewById(R.id.keep_showing).performClick();
mNotificationInfo.handleCloseControls(true, false);
@@ -708,10 +1057,21 @@
@Test
public void testNoActionsUpdatesNotificationChannel_blockingHelper() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, true,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ true,
+ IMPORTANCE_DEFAULT,
+ true);
mNotificationInfo.handleCloseControls(true, false);
@@ -727,10 +1087,21 @@
@Test
public void testSilenceCallsUpdateNotificationChannel() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_DEFAULT, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
mNotificationInfo.findViewById(R.id.silence).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -749,10 +1120,21 @@
@Test
public void testUnSilenceCallsUpdateNotificationChannel() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_LOW, false);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_LOW,
+ false);
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -772,10 +1154,21 @@
public void testSilenceCallsUpdateNotificationChannel_channelImportanceUnspecified()
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_UNSPECIFIED, true);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_UNSPECIFIED,
+ true);
mNotificationInfo.findViewById(R.id.silence).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -795,10 +1188,21 @@
public void testSilenceCallsUpdateNotificationChannel_channelImportanceMin()
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_MIN);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_MIN, false);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_MIN,
+ false);
assertEquals(mContext.getString(R.string.inline_done_button),
((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -821,10 +1225,21 @@
public void testAlertCallsUpdateNotificationChannel_channelImportanceMin()
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_MIN);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_MIN, false);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_MIN,
+ false);
assertEquals(mContext.getString(R.string.inline_done_button),
((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -844,13 +1259,50 @@
}
@Test
+ public void testAdjustImportanceTemporarilyAllowsReordering() throws Exception {
+ mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_DEFAULT,
+ true);
+
+ mNotificationInfo.findViewById(R.id.silence).performClick();
+ mNotificationInfo.findViewById(R.id.done).performClick();
+ mNotificationInfo.handleCloseControls(true, false);
+
+ verify(mVisualStabilityManager).temporarilyAllowReordering();
+ }
+
+ @Test
public void testDoneText()
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_LOW, false);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_LOW,
+ false);
assertEquals(mContext.getString(R.string.inline_done_button),
((TextView) mNotificationInfo.findViewById(R.id.done)).getText());
@@ -866,10 +1318,21 @@
public void testUnSilenceCallsUpdateNotificationChannel_channelImportanceUnspecified()
throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_LOW, false);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_LOW,
+ false);
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -888,10 +1351,21 @@
@Test
public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn, null, null,
- null, true, false,
- IMPORTANCE_LOW, false);
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
+ null,
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_LOW,
+ false);
mNotificationInfo.findViewById(R.id.alert).performClick();
mNotificationInfo.findViewById(R.id.done).performClick();
@@ -905,11 +1379,23 @@
@Test
public void testCloseControlsUpdatesWhenCheckSaveListenerUsesCallback() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
- }, null, null, true, false, IMPORTANCE_LOW, false
+ },
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_LOW,
+ false
);
mNotificationInfo.findViewById(R.id.alert).performClick();
@@ -928,11 +1414,23 @@
@Test
public void testCloseControls_withoutHittingApply() throws Exception {
mNotificationChannel.setImportance(IMPORTANCE_LOW);
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
- }, null, null, true, false, IMPORTANCE_LOW, false
+ },
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_LOW,
+ false
);
mNotificationInfo.findViewById(R.id.alert).performClick();
@@ -944,11 +1442,23 @@
public void testWillBeRemovedReturnsFalse() throws Exception {
assertFalse(mNotificationInfo.willBeRemoved());
- mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
- TEST_PACKAGE_NAME, mNotificationChannel, mNotificationChannelSet, mSbn,
+ mNotificationInfo.bindNotification(
+ mMockPackageManager,
+ mMockINotificationManager,
+ mVisualStabilityManager,
+ TEST_PACKAGE_NAME,
+ mNotificationChannel,
+ mNotificationChannelSet,
+ mSbn,
(Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
- }, null, null, true, false, IMPORTANCE_LOW, false
+ },
+ null,
+ null,
+ true,
+ false,
+ IMPORTANCE_LOW,
+ false
);
assertFalse(mNotificationInfo.willBeRemoved());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
index df41a84..4f844f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapperTest.java
@@ -131,7 +131,7 @@
));
// Ensure the callback runs at least once
- mWrapper.mUpdatePlaybackUi.run();
+ mWrapper.mOnUpdateTimerTick.run();
verify(mMetricsLogger).write(argThat(logMaker ->
logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
diff --git a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_qs_bluetooth.xml b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_qs_bluetooth.xml
index 58800c8..18a60d8 100644
--- a/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_qs_bluetooth.xml
+++ b/packages/overlays/IconPackFilledAndroidOverlay/res/drawable/ic_qs_bluetooth.xml
@@ -22,5 +22,5 @@
android:width="24dp" >
<path
android:fillColor="@android:color/white"
- android:pathData="M17.21,6.79l-4.5-4.5c-0.29-0.29-0.72-0.37-1.09-0.22C11.25,2.23,11,2.6,11,3v6.59l-3.8-3.8c-0.39-0.39-1.02-0.39-1.41,0 c-0.39,0.39-0.39,1.02,0,1.41l4.8,4.8l-4.8,4.8c-0.39,0.39-0.39,1.02,0,1.41c0.39,0.39,1.02,0.39,1.41,0l3.8-3.8V21 c0,0.4,0.24,0.77,0.62,0.92C11.74,21.98,11.87,22,12,22c0.26,0,0.52-0.1,0.71-0.29l4.5-4.5c0.39-0.39,0.39-1.02,0-1.41L13.42,12 l3.79-3.79C17.6,7.82,17.6,7.18,17.21,6.79z M15.09,16.5L13,18.58v-4.17L15.09,16.5z M13,9.58V5.42l2.08,2.08L13,9.58z" />
+ android:pathData="M13.51,12l3.75-3.74c0.41-0.41,0.41-1.07,0-1.48l-4.47-4.47l-0.03-0.03c-0.42-0.39-1.08-0.37-1.48,0.05 C11.1,2.52,11,2.78,11,3.04v6.44L6.95,5.43c-0.41-0.41-1.06-0.41-1.47,0c-0.41,0.41-0.41,1.06,0,1.47l5.09,5.1l-5.09,5.09 c-0.41,0.41-0.41,1.06,0,1.47c0.41,0.41,1.06,0.41,1.47,0L11,14.51v6.45c0,0.57,0.47,1.04,1.04,1.04c0.26,0,0.52-0.1,0.71-0.28 l0.05-0.05l4.46-4.46c0.41-0.41,0.41-1.07,0-1.48L13.51,12z M12.99,5.37l2.15,2.15l-2.15,2.15V5.37z M12.99,18.62v-4.3l2.15,2.15 L12.99,18.62z" />
</vector>
\ No newline at end of file
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 66b5437..af9b0e2 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2178,6 +2178,7 @@
private void requestNewFillResponseOnViewEnteredIfNecessaryLocked(@NonNull AutofillId id,
@NonNull ViewState viewState, int flags) {
if ((flags & FLAG_MANUAL_REQUEST) != 0) {
+ mForAugmentedAutofillOnly = false;
if (sDebug) Slog.d(TAG, "Re-starting session on view " + id + " and flags " + flags);
requestNewFillResponseLocked(viewState, ViewState.STATE_RESTARTED_SESSION, flags);
return;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 45f7360..39ce4a0 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -91,6 +91,7 @@
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkMisc;
+import android.net.NetworkMonitorManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
@@ -169,6 +170,7 @@
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
+import com.android.server.connectivity.AutodestructReference;
import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.DnsManager;
import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
@@ -1785,8 +1787,7 @@
// caller type. Need to re-factor NetdEventListenerService to allow multiple
// NetworkMonitor registrants.
if (nai != null && nai.satisfies(mDefaultRequest)) {
- Binder.withCleanCallingIdentity(() ->
- nai.networkMonitor().notifyDnsResponse(returnCode));
+ nai.networkMonitor().notifyDnsResponse(returnCode);
}
}
@@ -2662,6 +2663,17 @@
NetworkAgent.CMD_REPORT_NETWORK_STATUS,
(valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
0, redirectUrlBundle);
+
+ // If NetworkMonitor detects partial connectivity before
+ // EVENT_PROMPT_UNVALIDATED arrives, show the partial connectivity notification
+ // immediately. Re-notify partial connectivity silently if no internet
+ // notification already there.
+ if (!wasPartial && nai.partialConnectivity) {
+ // Remove delayed message if there is a pending message.
+ mHandler.removeMessages(EVENT_PROMPT_UNVALIDATED, nai.network);
+ handlePromptUnvalidated(nai.network);
+ }
+
if (wasValidated && !nai.lastValidated) {
handleNetworkUnvalidated(nai);
}
@@ -2763,29 +2775,31 @@
}
private class NetworkMonitorCallbacks extends INetworkMonitorCallbacks.Stub {
- private final NetworkAgentInfo mNai;
+ private final int mNetId;
+ private final AutodestructReference<NetworkAgentInfo> mNai;
private NetworkMonitorCallbacks(NetworkAgentInfo nai) {
- mNai = nai;
+ mNetId = nai.network.netId;
+ mNai = new AutodestructReference(nai);
}
@Override
public void onNetworkMonitorCreated(INetworkMonitor networkMonitor) {
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT,
- new Pair<>(mNai, networkMonitor)));
+ new Pair<>(mNai.getAndDestroy(), networkMonitor)));
}
@Override
public void notifyNetworkTested(int testResult, @Nullable String redirectUrl) {
mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(EVENT_NETWORK_TESTED,
- testResult, mNai.network.netId, redirectUrl));
+ testResult, mNetId, redirectUrl));
}
@Override
public void notifyPrivateDnsConfigResolved(PrivateDnsConfigParcel config) {
mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(
EVENT_PRIVATE_DNS_CONFIG_RESOLVED,
- 0, mNai.network.netId, PrivateDnsConfig.fromParcel(config)));
+ 0, mNetId, PrivateDnsConfig.fromParcel(config)));
}
@Override
@@ -2803,15 +2817,13 @@
}
mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(
EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_SHOW,
- mNai.network.netId,
- pendingIntent));
+ mNetId, pendingIntent));
}
@Override
public void hideProvisioningNotification() {
mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(
- EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE,
- mNai.network.netId));
+ EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE, mNetId));
}
@Override
@@ -2853,11 +2865,7 @@
// Notify the NetworkAgentInfo/NetworkMonitor in case NetworkMonitor needs to cancel or
// schedule DNS resolutions. If a DNS resolution is required the
// result will be sent back to us.
- try {
- nai.networkMonitor().notifyPrivateDnsChanged(cfg.toParcel());
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
+ nai.networkMonitor().notifyPrivateDnsChanged(cfg.toParcel());
// With Private DNS bypass support, we can proceed to update the
// Private DNS config immediately, even if we're in strict mode
@@ -3023,11 +3031,7 @@
// Disable wakeup packet monitoring for each interface.
wakeupModifyInterface(iface, nai.networkCapabilities, false);
}
- try {
- nai.networkMonitor().notifyNetworkDisconnected();
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
+ nai.networkMonitor().notifyNetworkDisconnected();
mNetworkAgentInfos.remove(nai.messenger);
nai.clatd.update();
synchronized (mNetworkForNetId) {
@@ -3440,11 +3444,7 @@
//
// TODO: NetworkMonitor does not refer to the "never ask again" bit. The bit is stored
// per network. Therefore, NetworkMonitor may still do https probe.
- try {
- nai.networkMonitor().setAcceptPartialConnectivity();
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
+ nai.networkMonitor().setAcceptPartialConnectivity();
}
}
@@ -3476,11 +3476,7 @@
NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
if (nai == null) return;
if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) return;
- try {
- nai.networkMonitor().launchCaptivePortalApp();
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
+ nai.networkMonitor().launchCaptivePortalApp();
});
}
@@ -3515,7 +3511,7 @@
}
@Override
- public void appResponse(final int response) throws RemoteException {
+ public void appResponse(final int response) {
if (response == CaptivePortal.APP_RETURN_WANTED_AS_IS) {
enforceSettingsPermission();
}
@@ -3525,16 +3521,9 @@
if (nai == null) return;
// nai.networkMonitor() is thread-safe
- final INetworkMonitor nm = nai.networkMonitor();
+ final NetworkMonitorManager nm = nai.networkMonitor();
if (nm == null) return;
-
- final long token = Binder.clearCallingIdentity();
- try {
- nm.notifyCaptivePortalAppFinished(response);
- } finally {
- // Not using Binder.withCleanCallingIdentity() to keep the checked RemoteException
- Binder.restoreCallingIdentity(token);
- }
+ nm.notifyCaptivePortalAppFinished(response);
}
@Override
@@ -3660,6 +3649,13 @@
|| nai.networkMisc.acceptPartialConnectivity) {
return;
}
+
+ // Stop automatically reconnecting to this network in the future. Automatically connecting
+ // to a network that provides no or limited connectivity is not useful, because the user
+ // cannot use that network except through the notification shown by this method, and the
+ // notification is only shown if the network is explicitly selected by the user.
+ nai.asyncChannel.sendMessage(NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT);
+
// TODO: Evaluate if it's needed to wait 8 seconds for triggering notification when
// NetworkMonitor detects the network is partial connectivity. Need to change the design to
// popup the notification immediately when the network is partial connectivity.
@@ -4105,11 +4101,7 @@
if (isNetworkWithLinkPropertiesBlocked(lp, uid, false)) {
return;
}
- try {
- nai.networkMonitor().forceReevaluation(uid);
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
+ nai.networkMonitor().forceReevaluation(uid);
}
/**
@@ -5542,11 +5534,7 @@
// Start or stop DNS64 detection and 464xlat according to network state.
networkAgent.clatd.update();
notifyIfacesChangedForNetworkStats();
- try {
- networkAgent.networkMonitor().notifyLinkPropertiesChanged(newLp);
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
+ networkAgent.networkMonitor().notifyLinkPropertiesChanged(newLp);
if (networkAgent.everConnected) {
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
}
@@ -6530,15 +6518,11 @@
// command must be sent after updating LinkProperties to maximize chances of
// NetworkMonitor seeing the correct LinkProperties when starting.
// TODO: pass LinkProperties to the NetworkMonitor in the notifyNetworkConnected call.
- try {
- if (networkAgent.networkMisc.acceptPartialConnectivity) {
- networkAgent.networkMonitor().setAcceptPartialConnectivity();
- }
- networkAgent.networkMonitor().notifyNetworkConnected(
- networkAgent.linkProperties, networkAgent.networkCapabilities);
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
+ if (networkAgent.networkMisc.acceptPartialConnectivity) {
+ networkAgent.networkMonitor().setAcceptPartialConnectivity();
}
+ networkAgent.networkMonitor().notifyNetworkConnected(
+ networkAgent.linkProperties, networkAgent.networkCapabilities);
scheduleUnvalidatedPrompt(networkAgent);
// Whether a particular NetworkRequest listen should cause signal strength thresholds to
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index e097d85..4e416a2 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -63,7 +63,7 @@
static final String TAG = "Watchdog";
/** Debug flag. */
- public static final boolean DEBUG = true; // STOPSHIP disable it (b/113252928)
+ public static final boolean DEBUG = false;
// Set this to true to use debug default values.
static final boolean DB = false;
@@ -570,7 +570,7 @@
continue;
} else if (waitState == WAITED_HALF) {
if (!waitedHalf) {
- if (DEBUG) Slog.d(TAG, "WAITED_HALF");
+ Slog.i(TAG, "WAITED_HALF");
// We've waited half the deadlock-detection interval. Pull a stack
// trace and wait another half.
ArrayList<Integer> pids = new ArrayList<Integer>();
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index fae853c..7bc2e6d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1946,8 +1946,6 @@
r.binding.service.app.hasClientActivities()
|| r.binding.service.app.treatLikeActivity, null);
}
- mAm.updateOomAdjLocked(r.binding.service.app, false,
- OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
}
}
@@ -2906,15 +2904,15 @@
// Tell the service that it has been unbound.
if (r.app != null && r.app.thread != null) {
- for (int i=r.bindings.size()-1; i>=0; i--) {
+ boolean needOomAdj = false;
+ for (int i = r.bindings.size() - 1; i >= 0; i--) {
IntentBindRecord ibr = r.bindings.valueAt(i);
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bringing down binding " + ibr
+ ": hasBound=" + ibr.hasBound);
if (ibr.hasBound) {
try {
bumpServiceExecutingLocked(r, false, "bring down unbind");
- mAm.updateOomAdjLocked(r.app, true,
- OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
+ needOomAdj = true;
ibr.hasBound = false;
ibr.requested = false;
r.app.thread.scheduleUnbindService(r,
@@ -2926,6 +2924,10 @@
}
}
}
+ if (needOomAdj) {
+ mAm.updateOomAdjLocked(r.app, true,
+ OomAdjuster.OOM_ADJ_REASON_UNBIND_SERVICE);
+ }
}
// Check to see if the service had been started as foreground, but being
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0619eb9..2321031 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18156,13 +18156,20 @@
}
}
+ // The arguments here are untyped because the base ActivityManagerInternal class
+ // doesn't have compile-time visiblity into ActivityServiceConnectionHolder or
+ // ConnectionRecord.
@Override
- public void disconnectActivityFromServices(Object connectionHolder) {
+ public void disconnectActivityFromServices(Object connectionHolder, Object conns) {
+ // 'connectionHolder' is an untyped ActivityServiceConnectionsHolder
+ // 'conns' is an untyped HashSet<ConnectionRecord>
+ final ActivityServiceConnectionsHolder holder =
+ (ActivityServiceConnectionsHolder) connectionHolder;
+ final HashSet<ConnectionRecord> toDisconnect = (HashSet<ConnectionRecord>) conns;
synchronized(ActivityManagerService.this) {
- final ActivityServiceConnectionsHolder c =
- (ActivityServiceConnectionsHolder) connectionHolder;
- c.forEachConnection(cr -> mServices.removeConnectionLocked(
- (ConnectionRecord) cr, null, c));
+ for (ConnectionRecord cr : toDisconnect) {
+ mServices.removeConnectionLocked(cr, null, holder);
+ }
}
}
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 69a1c9f5..805cac7 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -108,6 +108,12 @@
* must be called with the mInMemoryLock, xxxDMLocked suffix means the method
* must be called with the mOnDiskLock and mInMemoryLock locks acquired in that
* exact order.
+ * <p>
+ * INITIALIZATION: We can initialize persistence only after the system is ready
+ * as we need to check the optional configuration override from the settings
+ * database which is not initialized at the time the app ops service is created.
+ * This means that all entry points that touch persistence should be short
+ * circuited via isPersistenceInitialized() check.
*/
// TODO (bug:122218838): Make sure we handle start of epoch time
// TODO (bug:122218838): Validate changed time is handled correctly
@@ -177,14 +183,33 @@
// Object managing persistence (read/write)
@GuardedBy("mOnDiskLock")
- private Persistence mPersistence = new Persistence(mBaseSnapshotInterval,
- mIntervalCompressionMultiplier);
+ private Persistence mPersistence;
HistoricalRegistry(@NonNull Object lock) {
mInMemoryLock = lock;
- if (mMode != AppOpsManager.HISTORICAL_MODE_DISABLED) {
- synchronized (mOnDiskLock) {
- synchronized (mInMemoryLock) {
+ }
+
+ void systemReady(@NonNull ContentResolver resolver) {
+ final Uri uri = Settings.Global.getUriFor(Settings.Global.APPOP_HISTORY_PARAMETERS);
+ resolver.registerContentObserver(uri, false, new ContentObserver(
+ FgThread.getHandler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateParametersFromSetting(resolver);
+ }
+ });
+
+ updateParametersFromSetting(resolver);
+
+ synchronized (mOnDiskLock) {
+ synchronized (mInMemoryLock) {
+ if (mMode != AppOpsManager.HISTORICAL_MODE_DISABLED) {
+ // Can be uninitialized if there is no config in the settings table.
+ if (!isPersistenceInitializedMLocked()) {
+ mPersistence = new Persistence(mBaseSnapshotInterval,
+ mIntervalCompressionMultiplier);
+ }
+
// When starting always adjust history to now.
final long lastPersistTimeMills =
mPersistence.getLastPersistTimeMillisDLocked();
@@ -197,16 +222,8 @@
}
}
- void systemReady(@NonNull ContentResolver resolver) {
- updateParametersFromSetting(resolver);
- final Uri uri = Settings.Global.getUriFor(Settings.Global.APPOP_HISTORY_PARAMETERS);
- resolver.registerContentObserver(uri, false, new ContentObserver(
- FgThread.getHandler()) {
- @Override
- public void onChange(boolean selfChange) {
- updateParametersFromSetting(resolver);
- }
- });
+ private boolean isPersistenceInitializedMLocked() {
+ return mPersistence != null;
}
private void updateParametersFromSetting(@NonNull ContentResolver resolver) {
@@ -274,6 +291,11 @@
makeRelativeToEpochStart(currentOps, nowMillis);
currentOps.accept(visitor);
+ if(isPersistenceInitializedMLocked()) {
+ Slog.e(LOG_TAG, "Interaction before persistence initialized");
+ return;
+ }
+
final List<HistoricalOps> ops = mPersistence.readHistoryDLocked();
if (ops != null) {
// TODO (bug:122218838): Make sure this is properly dumped
@@ -302,12 +324,21 @@
void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName,
@Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
@OpFlags int flags, @NonNull RemoteCallback callback) {
- final HistoricalOps result = new HistoricalOps(beginTimeMillis, endTimeMillis);
- mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, opNames,
- beginTimeMillis, endTimeMillis, flags);
- final Bundle payload = new Bundle();
- payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result);
- callback.sendResult(payload);
+ synchronized (mOnDiskLock) {
+ synchronized (mInMemoryLock) {
+ if (!isPersistenceInitializedMLocked()) {
+ Slog.e(LOG_TAG, "Interaction before persistence initialized");
+ callback.sendResult(new Bundle());
+ return;
+ }
+ final HistoricalOps result = new HistoricalOps(beginTimeMillis, endTimeMillis);
+ mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, opNames,
+ beginTimeMillis, endTimeMillis, flags);
+ final Bundle payload = new Bundle();
+ payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result);
+ callback.sendResult(payload);
+ }
+ }
}
void getHistoricalOps(int uid, @NonNull String packageName,
@@ -331,6 +362,12 @@
boolean collectOpsFromDisk;
synchronized (mInMemoryLock) {
+ if (!isPersistenceInitializedMLocked()) {
+ Slog.e(LOG_TAG, "Interaction before persistence initialized");
+ callback.sendResult(new Bundle());
+ return;
+ }
+
currentOps = getUpdatedPendingHistoricalOpsMLocked(currentTimeMillis);
if (!(inMemoryAdjBeginTimeMillis >= currentOps.getEndTimeMillis()
|| inMemoryAdjEndTimeMillis <= currentOps.getBeginTimeMillis())) {
@@ -374,6 +411,10 @@
@UidState int uidState, @OpFlags int flags) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
+ if (!isPersistenceInitializedMLocked()) {
+ Slog.e(LOG_TAG, "Interaction before persistence initialized");
+ return;
+ }
getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis())
.increaseAccessCount(op, uid, packageName, uidState, flags, 1);
}
@@ -384,6 +425,10 @@
@UidState int uidState, @OpFlags int flags) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
+ if (!isPersistenceInitializedMLocked()) {
+ Slog.e(LOG_TAG, "Interaction before persistence initialized");
+ return;
+ }
getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis())
.increaseRejectCount(op, uid, packageName, uidState, flags, 1);
}
@@ -394,6 +439,10 @@
@UidState int uidState, @OpFlags int flags, long increment) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
+ if (!isPersistenceInitializedMLocked()) {
+ Slog.e(LOG_TAG, "Interaction before persistence initialized");
+ return;
+ }
getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis())
.increaseAccessDuration(op, uid, packageName, uidState, flags, increment);
}
@@ -404,6 +453,8 @@
long baseSnapshotInterval, long intervalCompressionMultiplier) {
synchronized (mOnDiskLock) {
synchronized (mInMemoryLock) {
+ // NOTE: We allow this call if persistence is not initialized as
+ // it is a part of the persistence initialization process.
boolean resampleHistory = false;
Slog.i(LOG_TAG, "New history parameters: mode:"
+ AppOpsManager.historicalModeToString(mMode) + " baseSnapshotInterval:"
@@ -412,7 +463,7 @@
if (mMode != mode) {
mMode = mode;
if (mMode == AppOpsManager.HISTORICAL_MODE_DISABLED) {
- clearHistoryOnDiskLocked();
+ clearHistoryOnDiskDLocked();
}
}
if (mBaseSnapshotInterval != baseSnapshotInterval) {
@@ -433,6 +484,10 @@
void offsetHistory(long offsetMillis) {
synchronized (mOnDiskLock) {
synchronized (mInMemoryLock) {
+ if (!isPersistenceInitializedMLocked()) {
+ Slog.e(LOG_TAG, "Interaction before persistence initialized");
+ return;
+ }
final List<HistoricalOps> history = mPersistence.readHistoryDLocked();
clearHistory();
if (history != null) {
@@ -453,6 +508,10 @@
void addHistoricalOps(HistoricalOps ops) {
final List<HistoricalOps> pendingWrites;
synchronized (mInMemoryLock) {
+ if (!isPersistenceInitializedMLocked()) {
+ Slog.e(LOG_TAG, "Interaction before persistence initialized");
+ return;
+ }
// The history files start from mBaseSnapshotInterval - take this into account.
ops.offsetBeginAndEndTime(mBaseSnapshotInterval);
mPendingWrites.offerFirst(ops);
@@ -468,6 +527,10 @@
}
void resetHistoryParameters() {
+ if (!isPersistenceInitializedMLocked()) {
+ Slog.e(LOG_TAG, "Interaction before persistence initialized");
+ return;
+ }
setHistoryParameters(DEFAULT_MODE, DEFAULT_SNAPSHOT_INTERVAL_MILLIS,
DEFAULT_COMPRESSION_STEP);
}
@@ -475,6 +538,10 @@
void clearHistory(int uid, String packageName) {
synchronized (mOnDiskLock) {
synchronized (mInMemoryLock) {
+ if (!isPersistenceInitializedMLocked()) {
+ Slog.e(LOG_TAG, "Interaction before persistence initialized");
+ return;
+ }
if (mMode != AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
return;
}
@@ -493,11 +560,17 @@
void clearHistory() {
synchronized (mOnDiskLock) {
- clearHistoryOnDiskLocked();
+ synchronized (mInMemoryLock) {
+ if (!isPersistenceInitializedMLocked()) {
+ Slog.e(LOG_TAG, "Interaction before persistence initialized");
+ return;
+ }
+ clearHistoryOnDiskDLocked();
+ }
}
}
- private void clearHistoryOnDiskLocked() {
+ private void clearHistoryOnDiskDLocked() {
BackgroundThread.getHandler().removeMessages(MSG_WRITE_PENDING_HISTORY);
synchronized (mInMemoryLock) {
mCurrentHistoricalOps = null;
@@ -718,13 +791,27 @@
baseDir = mHistoricalAppOpsDir.startRead();
final File[] files = baseDir.listFiles();
if (files != null && files.length > 0) {
- final Set<File> historyFiles = new ArraySet<>();
- Collections.addAll(historyFiles, files);
- for (int i = 0;; i++) {
- final File file = generateFile(baseDir, i);
- if (historyFiles.contains(file)) {
- return file.lastModified();
+ File shortestFile = null;
+ for (File candidate : files) {
+ final String candidateName = candidate.getName();
+ if (!candidateName.endsWith(HISTORY_FILE_SUFFIX)) {
+ continue;
}
+ if (shortestFile == null) {
+ shortestFile = candidate;
+ } else if (candidateName.length() < shortestFile.getName().length()) {
+ shortestFile = candidate;
+ }
+ }
+ if (shortestFile == null) {
+ return 0;
+ }
+ final String shortestNameNoExtension = shortestFile.getName()
+ .replace(HISTORY_FILE_SUFFIX, "");
+ try {
+ return Long.parseLong(shortestNameNoExtension);
+ } catch (NumberFormatException e) {
+ return 0;
}
}
mHistoricalAppOpsDir.finishRead();
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index fcd6a0a..884ecba 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -569,9 +569,8 @@
return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
}
- //###
// must be called synchronized on mConnectedDevices
- /*package*/ boolean hasScheduledA2dpSinkConnectionState(BluetoothDevice btDevice) {
+ /*package*/ boolean hasScheduledA2dpSinkConnectionState(BluetoothDevice btDevice) {
return mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE,
new BtHelper.BluetoothA2dpDeviceInfo(btDevice));
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 99b97cb..a9a8ef2 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -146,6 +146,7 @@
}
}
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ void onSetA2dpSinkConnectionState(@NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo,
@AudioService.BtProfileConnectionState int state) {
final BluetoothDevice btDevice = btInfo.getBtDevice();
@@ -259,6 +260,7 @@
}
}
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ void onBluetoothA2dpActiveDeviceChange(
@NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) {
final BluetoothDevice btDevice = btInfo.getBtDevice();
@@ -532,6 +534,7 @@
return mCurAudioRoutes;
}
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ void setBluetoothA2dpDeviceConnectionState(
@NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
int profile, boolean suppressNoisyIntent, int musicDevice, int a2dpVolume) {
@@ -559,9 +562,13 @@
final BtHelper.BluetoothA2dpDeviceInfo a2dpDeviceInfo =
new BtHelper.BluetoothA2dpDeviceInfo(device, a2dpVolume, a2dpCodec);
if (profile == BluetoothProfile.A2DP) {
- mDeviceBroker.postA2dpSinkConnection(state,
- a2dpDeviceInfo,
- delay);
+ if (delay == 0) {
+ onSetA2dpSinkConnectionState(a2dpDeviceInfo, state);
+ } else {
+ mDeviceBroker.postA2dpSinkConnection(state,
+ a2dpDeviceInfo,
+ delay);
+ }
} else { //profile == BluetoothProfile.A2DP_SINK
mDeviceBroker.postA2dpSourceConnection(state,
a2dpDeviceInfo,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 30035c8..e43c548 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -162,6 +162,7 @@
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* The implementation of the volume manager service.
@@ -265,6 +266,7 @@
private static final int MSG_SET_DEVICE_STREAM_VOLUME = 26;
private static final int MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS = 27;
private static final int MSG_HDMI_VOLUME_CHECK = 28;
+ private static final int MSG_PLAYBACK_CONFIG_CHANGE = 29;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -1275,8 +1277,6 @@
System.VOLUME_SETTINGS_INT[a11yStreamAlias];
mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setAllIndexes(
mStreamStates[a11yStreamAlias], caller);
- mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].refreshRange(
- mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY]);
}
}
if (sIndependentA11yVolume) {
@@ -1577,20 +1577,19 @@
}
private int rescaleIndex(int index, int srcStream, int dstStream) {
- int max = mStreamStates[srcStream].getMaxIndex();
- if (max == 0) {
- Log.e(TAG, "rescaleIndex : Max index should not be zero");
- return mStreamStates[srcStream].getMinIndex();
- }
- final int rescaled =
- (index * mStreamStates[dstStream].getMaxIndex()
- + mStreamStates[srcStream].getMaxIndex() / 2)
- / mStreamStates[srcStream].getMaxIndex();
- if (rescaled < mStreamStates[dstStream].getMinIndex()) {
+ int srcRange =
+ mStreamStates[srcStream].getMaxIndex() - mStreamStates[srcStream].getMinIndex();
+ int dstRange =
+ mStreamStates[dstStream].getMaxIndex() - mStreamStates[dstStream].getMinIndex();
+
+ if (srcRange == 0) {
+ Log.e(TAG, "rescaleIndex : index range should not be zero");
return mStreamStates[dstStream].getMinIndex();
- } else {
- return rescaled;
}
+
+ return mStreamStates[dstStream].getMinIndex()
+ + ((index - mStreamStates[srcStream].getMinIndex()) * dstRange + srcRange / 2)
+ / srcRange;
}
///////////////////////////////////////////////////////////////////////////
@@ -1870,9 +1869,15 @@
// Check if volume update should be send to Hearing Aid
if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
- Log.i(TAG, "adjustSreamVolume postSetHearingAidVolumeIndex index=" + newIndex
- + " stream=" + streamType);
- mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType);
+ // only modify the hearing aid attenuation when the stream to modify matches
+ // the one expected by the hearing aid
+ if (streamType == getHearingAidStreamType()) {
+ if (DEBUG_VOL) {
+ Log.d(TAG, "adjustSreamVolume postSetHearingAidVolumeIndex index="
+ + newIndex + " stream=" + streamType);
+ }
+ mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType);
+ }
}
// Check if volume update should be sent to Hdmi system audio.
@@ -2164,11 +2169,53 @@
return AudioSystem.STREAM_VOICE_CALL;
case AudioSystem.MODE_NORMAL:
default:
+ // other conditions will influence the stream type choice, read on...
break;
}
+ if (mVoiceActive.get()) {
+ return AudioSystem.STREAM_VOICE_CALL;
+ }
return AudioSystem.STREAM_MUSIC;
}
+ private AtomicBoolean mVoiceActive = new AtomicBoolean(false);
+
+ private final IPlaybackConfigDispatcher mVoiceActivityMonitor =
+ new IPlaybackConfigDispatcher.Stub() {
+ @Override
+ public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs,
+ boolean flush) {
+ sendMsg(mAudioHandler, MSG_PLAYBACK_CONFIG_CHANGE, SENDMSG_REPLACE,
+ 0 /*arg1 ignored*/, 0 /*arg2 ignored*/,
+ configs /*obj*/, 0 /*delay*/);
+ }
+ };
+
+ private void onPlaybackConfigChange(List<AudioPlaybackConfiguration> configs) {
+ boolean voiceActive = false;
+ for (AudioPlaybackConfiguration config : configs) {
+ final int usage = config.getAudioAttributes().getUsage();
+ if ((usage == AudioAttributes.USAGE_VOICE_COMMUNICATION
+ || usage == AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
+ && config.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
+ voiceActive = true;
+ break;
+ }
+ }
+ if (mVoiceActive.getAndSet(voiceActive) != voiceActive) {
+ updateHearingAidVolumeOnVoiceActivityUpdate();
+ }
+ }
+
+ private void updateHearingAidVolumeOnVoiceActivityUpdate() {
+ final int streamType = getHearingAidStreamType();
+ final int index = getStreamVolume(streamType);
+ sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_HEARING_AID,
+ mVoiceActive.get(), streamType, index));
+ mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType);
+
+ }
+
/**
* Manage an audio mode change for audio devices that use an "absolute volume" model,
* i.e. the framework sends the full scale signal, and the actual volume for the use case
@@ -2203,10 +2250,8 @@
// handling of specific interfaces goes here:
if ((device & mAbsVolumeMultiModeCaseDevices) == AudioSystem.DEVICE_OUT_HEARING_AID) {
final int index = getStreamVolume(streamType);
- mModeLogger.log(new AudioEventLogger.StringEvent("setMode to "
- + AudioSystem.modeToString(newMode)
- + " causes setting HEARING_AID volume to idx:" + index
- + " stream:" + AudioSystem.streamToString(streamType)));
+ sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_MODE_CHANGE_HEARING_AID,
+ newMode, streamType, index));
mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType);
}
}
@@ -2272,7 +2317,8 @@
mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index / 10);
}
- if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0) {
+ if ((device & AudioSystem.DEVICE_OUT_HEARING_AID) != 0
+ && streamType == getHearingAidStreamType()) {
Log.i(TAG, "setStreamVolume postSetHearingAidVolumeIndex index=" + index
+ " stream=" + streamType);
mDeviceBroker.postSetHearingAidVolumeIndex(index, streamType);
@@ -4348,6 +4394,11 @@
throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
+ " (dis)connection, got " + state);
}
+ if (state == BluetoothProfile.STATE_CONNECTED) {
+ mPlaybackMonitor.registerPlaybackCallback(mVoiceActivityMonitor, true);
+ } else {
+ mPlaybackMonitor.unregisterPlaybackCallback(mVoiceActivityMonitor);
+ }
mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
device, state, suppressNoisyIntent, musicDevice, "AudioService");
}
@@ -4730,24 +4781,6 @@
}
/**
- * Updates the min/max index values from another stream. Use this when changing the alias
- * for the current stream type.
- * @param sourceStreamType
- */
- // must be sync'd on mSettingsLock before VolumeStreamState.class
- @GuardedBy("VolumeStreamState.class")
- public void refreshRange(int sourceStreamType) {
- mIndexMin = MIN_STREAM_VOLUME[sourceStreamType] * 10;
- mIndexMax = MAX_STREAM_VOLUME[sourceStreamType] * 10;
- // verify all current volumes are within bounds
- for (int i = 0 ; i < mIndexMap.size(); i++) {
- final int device = mIndexMap.keyAt(i);
- final int index = mIndexMap.valueAt(i);
- mIndexMap.put(device, getValidIndex(index));
- }
- }
-
- /**
* Copies all device/index pairs from the given VolumeStreamState after initializing
* them with the volume for DEVICE_OUT_DEFAULT. No-op if the source VolumeStreamState
* has the same stream type as this instance.
@@ -4853,6 +4886,7 @@
pw.println((mIndexMin + 5) / 10);
pw.print(" Max: ");
pw.println((mIndexMax + 5) / 10);
+ pw.print(" streamVolume:"); pw.println(getStreamVolume(mStreamType));
pw.print(" Current: ");
for (int i = 0; i < mIndexMap.size(); i++) {
if (i > 0) {
@@ -5429,6 +5463,11 @@
case MSG_HDMI_VOLUME_CHECK:
onCheckVolumeCecOnHdmiConnection(msg.arg1, (String) msg.obj);
+ break;
+
+ case MSG_PLAYBACK_CONFIG_CHANGE:
+ onPlaybackConfigChange((List<AudioPlaybackConfiguration>) msg.obj);
+ break;
}
}
}
@@ -5508,6 +5547,8 @@
public void avrcpSupportsAbsoluteVolume(String address, boolean support) {
// address is not used for now, but may be used when multiple a2dp devices are supported
+ sVolumeLogger.log(new AudioEventLogger.StringEvent("avrcpSupportsAbsoluteVolume addr="
+ + address + " support=" + support));
mDeviceBroker.setAvrcpAbsoluteVolumeSupported(support);
sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index d999217..fcd8701 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -95,6 +95,8 @@
static final int VOL_SET_HEARING_AID_VOL = 3;
static final int VOL_SET_AVRCP_VOL = 4;
static final int VOL_ADJUST_VOL_UID = 5;
+ static final int VOL_VOICE_ACTIVITY_HEARING_AID = 6;
+ static final int VOL_MODE_CHANGE_HEARING_AID = 7;
final int mOp;
final int mStream;
@@ -102,6 +104,10 @@
final int mVal2;
final String mCaller;
+ /** used for VOL_ADJUST_VOL_UID,
+ * VOL_ADJUST_SUGG_VOL,
+ * VOL_ADJUST_STREAM_VOL,
+ * VOL_SET_STREAM_VOL */
VolumeEvent(int op, int stream, int val1, int val2, String caller) {
mOp = op;
mStream = stream;
@@ -110,24 +116,46 @@
mCaller = caller;
}
+ /** used for VOL_SET_HEARING_AID_VOL*/
VolumeEvent(int op, int index, int gainDb) {
mOp = op;
mVal1 = index;
mVal2 = gainDb;
- //unused
+ // unused
mStream = -1;
mCaller = null;
}
+ /** used for VOL_SET_AVRCP_VOL */
VolumeEvent(int op, int index) {
mOp = op;
mVal1 = index;
- //unused
+ // unused
mVal2 = 0;
mStream = -1;
mCaller = null;
}
+ /** used for VOL_VOICE_ACTIVITY_HEARING_AID */
+ VolumeEvent(int op, boolean voiceActive, int stream, int index) {
+ mOp = op;
+ mStream = stream;
+ mVal1 = index;
+ mVal2 = voiceActive ? 1 : 0;
+ // unused
+ mCaller = null;
+ }
+
+ /** used for VOL_MODE_CHANGE_HEARING_AID */
+ VolumeEvent(int op, int mode, int stream, int index) {
+ mOp = op;
+ mStream = stream;
+ mVal1 = index;
+ mVal2 = mode;
+ // unused
+ mCaller = null;
+ }
+
@Override
public String eventToString() {
switch (mOp) {
@@ -168,7 +196,19 @@
.append(" flags:0x").append(Integer.toHexString(mVal2))
.append(") from ").append(mCaller)
.toString();
- default: return new StringBuilder("FIXME invalid op:").append(mOp).toString();
+ case VOL_VOICE_ACTIVITY_HEARING_AID:
+ return new StringBuilder("Voice activity change (")
+ .append(mVal2 == 1 ? "active" : "inactive")
+ .append(") causes setting HEARING_AID volume to idx:").append(mVal1)
+ .append(" stream:").append(AudioSystem.streamToString(mStream))
+ .toString();
+ case VOL_MODE_CHANGE_HEARING_AID:
+ return new StringBuilder("setMode(")
+ .append(AudioSystem.modeToString(mVal2))
+ .append(") causes setting HEARING_AID volume to idx:").append(mVal1)
+ .append(" stream:").append(AudioSystem.streamToString(mStream))
+ .toString();
+ default: return new StringBuilder("FIXME invalid op:").append(mOp).toString();
}
}
}
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 934c7bf..1a63f8f 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -200,16 +200,20 @@
/*package*/ synchronized void setAvrcpAbsoluteVolumeSupported(boolean supported) {
mAvrcpAbsVolSupported = supported;
+ Log.i(TAG, "setAvrcpAbsoluteVolumeSupported supported=" + supported);
}
/*package*/ synchronized void setAvrcpAbsoluteVolumeIndex(int index) {
if (mA2dp == null) {
if (AudioService.DEBUG_VOL) {
- Log.d(TAG, "setAvrcpAbsoluteVolumeIndex: bailing due to null mA2dp");
+ AudioService.sVolumeLogger.log(new AudioEventLogger.StringEvent(
+ "setAvrcpAbsoluteVolumeIndex: bailing due to null mA2dp").printLog(TAG));
return;
}
}
if (!mAvrcpAbsVolSupported) {
+ AudioService.sVolumeLogger.log(new AudioEventLogger.StringEvent(
+ "setAvrcpAbsoluteVolumeIndex: abs vol not supported ").printLog(TAG));
return;
}
if (AudioService.DEBUG_VOL) {
diff --git a/services/core/java/com/android/server/connectivity/AutodestructReference.java b/services/core/java/com/android/server/connectivity/AutodestructReference.java
new file mode 100644
index 0000000..009a43e
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/AutodestructReference.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.annotation.NonNull;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A ref that autodestructs at the first usage of it.
+ * @param <T> The type of the held object
+ * @hide
+ */
+public class AutodestructReference<T> {
+ private final AtomicReference<T> mHeld;
+ public AutodestructReference(@NonNull T obj) {
+ if (null == obj) throw new NullPointerException("Autodestruct reference to null");
+ mHeld = new AtomicReference<>(obj);
+ }
+
+ /** Get the ref and destruct it. NPE if already destructed. */
+ @NonNull
+ public T getAndDestroy() {
+ final T obj = mHeld.getAndSet(null);
+ if (null == obj) throw new NullPointerException("Already autodestructed");
+ return obj;
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 34772d0..864a793 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -25,12 +25,12 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkMisc;
+import android.net.NetworkMonitorManager;
import android.net.NetworkRequest;
import android.net.NetworkState;
import android.os.Handler;
import android.os.INetworkManagementService;
import android.os.Messenger;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
@@ -247,7 +247,7 @@
public final Nat464Xlat clatd;
// Set after asynchronous creation of the NetworkMonitor.
- private volatile INetworkMonitor mNetworkMonitor;
+ private volatile NetworkMonitorManager mNetworkMonitor;
private static final String TAG = ConnectivityService.class.getSimpleName();
private static final boolean VDBG = false;
@@ -278,7 +278,7 @@
* Inform NetworkAgentInfo that a new NetworkMonitor was created.
*/
public void onNetworkMonitorCreated(INetworkMonitor networkMonitor) {
- mNetworkMonitor = networkMonitor;
+ mNetworkMonitor = new NetworkMonitorManager(networkMonitor);
}
/**
@@ -290,13 +290,9 @@
*/
public void setNetworkCapabilities(NetworkCapabilities nc) {
networkCapabilities = nc;
- final INetworkMonitor nm = mNetworkMonitor;
+ final NetworkMonitorManager nm = mNetworkMonitor;
if (nm != null) {
- try {
- nm.notifyNetworkCapabilitiesChanged(nc);
- } catch (RemoteException e) {
- Log.e(TAG, "Error notifying NetworkMonitor of updated NetworkCapabilities", e);
- }
+ nm.notifyNetworkCapabilitiesChanged(nc);
}
}
@@ -317,11 +313,11 @@
}
/**
- * Get the INetworkMonitor in this NetworkAgentInfo.
+ * Get the NetworkMonitorManager in this NetworkAgentInfo.
*
* <p>This will be null before {@link #onNetworkMonitorCreated(INetworkMonitor)} is called.
*/
- public INetworkMonitor networkMonitor() {
+ public NetworkMonitorManager networkMonitor() {
return mNetworkMonitor;
}
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 0940a2e..fee60d0 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -407,7 +407,8 @@
// the other.
public static final int PRIORITY_APP_REQUEST_REFRESH_RATE = 1;
public static final int PRIORITY_APP_REQUEST_SIZE = 2;
- public static final int PRIORITY_LOW_POWER_MODE = 3;
+ public static final int PRIORITY_LOW_BRIGHTNESS = 3;
+ public static final int PRIORITY_LOW_POWER_MODE = 4;
// Whenever a new priority is added, remember to update MIN_PRIORITY and/or MAX_PRIORITY as
// appropriate, as well as priorityToString.
@@ -485,15 +486,20 @@
Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
private final Uri mLowPowerModeSetting =
Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE);
+ private final Uri mBrightnessSetting =
+ Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
private final Context mContext;
private final float mDefaultPeakRefreshRate;
+ private final int mBrightnessThreshold;
SettingsObserver(@NonNull Context context, @NonNull Handler handler) {
super(handler);
mContext = context;
mDefaultPeakRefreshRate = (float) context.getResources().getInteger(
R.integer.config_defaultPeakRefreshRate);
+ mBrightnessThreshold = context.getResources().getInteger(
+ R.integer.config_brightnessThresholdOfPeakRefreshRate);
}
public void observe() {
@@ -502,9 +508,14 @@
UserHandle.USER_SYSTEM);
cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
UserHandle.USER_SYSTEM);
+ if (mBrightnessThreshold >= 0) {
+ cr.registerContentObserver(mBrightnessSetting, false /*notifyDescendants*/, this,
+ UserHandle.USER_SYSTEM);
+ }
synchronized (mLock) {
updateRefreshRateSettingLocked();
updateLowPowerModeSettingLocked();
+ updateBrightnessSettingLocked();
}
}
@@ -515,6 +526,8 @@
updateRefreshRateSettingLocked();
} else if (mLowPowerModeSetting.equals(uri)) {
updateLowPowerModeSettingLocked();
+ } else if (mBrightnessThreshold >=0 && mBrightnessSetting.equals(uri)) {
+ updateBrightnessSettingLocked();
}
}
}
@@ -538,6 +551,23 @@
updateVoteLocked(Vote.PRIORITY_USER_SETTING, vote);
}
+ private void updateBrightnessSettingLocked() {
+ int brightness = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS, -1);
+
+ if (brightness < 0) {
+ return;
+ }
+
+ final Vote vote;
+ if (brightness <= mBrightnessThreshold) {
+ vote = Vote.forRefreshRates(0f, 60f);
+ } else {
+ vote = null;
+ }
+ updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote);
+ }
+
public void dumpLocked(PrintWriter pw) {
pw.println(" SettingsObserver");
pw.println(" mDefaultPeakRefreshRate: " + mDefaultPeakRefreshRate);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 5fb67dd..5804fc8 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -309,6 +309,13 @@
private boolean mAppliedTemporaryAutoBrightnessAdjustment;
private boolean mAppliedBrightnessBoost;
+ // Reason for which the brightness was last changed. See {@link BrightnessReason} for more
+ // information.
+ // At the time of this writing, this value is changed within updatePowerState() only, which is
+ // limited to the thread used by DisplayControllerHandler.
+ private BrightnessReason mBrightnessReason = new BrightnessReason();
+ private BrightnessReason mBrightnessReasonTemp = new BrightnessReason();
+
// Brightness animation ramp rates in brightness units per second
private final int mBrightnessRampRateFast;
private final int mBrightnessRampRateSlow;
@@ -733,6 +740,8 @@
final boolean mustNotify;
final int previousPolicy;
boolean mustInitialize = false;
+ int brightnessAdjustmentFlags = 0;
+ mBrightnessReasonTemp.set(null);
synchronized (mLock) {
mPendingUpdatePowerStateLocked = false;
@@ -786,6 +795,7 @@
}
if (!mAllowAutoBrightnessWhileDozingConfig) {
brightness = mPowerRequest.dozeScreenBrightness;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE);
}
break;
case DisplayPowerRequest.POLICY_VR:
@@ -839,15 +849,18 @@
// Use zero brightness when screen is off.
if (state == Display.STATE_OFF) {
brightness = PowerManager.BRIGHTNESS_OFF;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_SCREEN_OFF);
}
// Always use the VR brightness when in the VR state.
if (state == Display.STATE_VR) {
brightness = mScreenBrightnessForVr;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_VR);
}
if (brightness < 0 && mPowerRequest.screenBrightnessOverride > 0) {
brightness = mPowerRequest.screenBrightnessOverride;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_OVERRIDE);
mAppliedScreenBrightnessOverride = true;
} else {
mAppliedScreenBrightnessOverride = false;
@@ -867,6 +880,7 @@
if (mTemporaryScreenBrightness > 0) {
brightness = mTemporaryScreenBrightness;
mAppliedTemporaryBrightness = true;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_TEMPORARY);
} else {
mAppliedTemporaryBrightness = false;
}
@@ -880,9 +894,11 @@
final float autoBrightnessAdjustment;
if (!Float.isNaN(mTemporaryAutoBrightnessAdjustment)) {
autoBrightnessAdjustment = mTemporaryAutoBrightnessAdjustment;
+ brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO_TEMP;
mAppliedTemporaryAutoBrightnessAdjustment = true;
} else {
autoBrightnessAdjustment = mAutoBrightnessAdjustment;
+ brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO;
mAppliedTemporaryAutoBrightnessAdjustment = false;
}
@@ -893,6 +909,7 @@
if (mPowerRequest.boostScreenBrightness
&& brightness != PowerManager.BRIGHTNESS_OFF) {
brightness = PowerManager.BRIGHTNESS_ON;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_BOOST);
mAppliedBrightnessBoost = true;
} else {
mAppliedBrightnessBoost = false;
@@ -936,6 +953,7 @@
// it means in absolute terms.
putScreenBrightnessSetting(brightness);
mAppliedAutoBrightness = true;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC);
} else {
mAppliedAutoBrightness = false;
}
@@ -943,19 +961,25 @@
// If the autobrightness controller has decided to change the adjustment value
// used, make sure that's reflected in settings.
putAutoBrightnessAdjustmentSetting(newAutoBrightnessAdjustment);
+ } else {
+ // Adjustment values resulted in no change
+ brightnessAdjustmentFlags = 0;
}
} else {
mAppliedAutoBrightness = false;
+ brightnessAdjustmentFlags = 0;
}
// Use default brightness when dozing unless overridden.
if (brightness < 0 && Display.isDozeState(state)) {
brightness = mScreenBrightnessDozeConfig;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
}
// Apply manual brightness.
if (brightness < 0) {
brightness = clampScreenBrightness(mCurrentScreenBrightnessSetting);
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL);
}
// Apply dimming by at least some minimum amount when user activity
@@ -964,6 +988,7 @@
if (brightness > mScreenBrightnessRangeMinimum) {
brightness = Math.max(Math.min(brightness - SCREEN_DIM_MINIMUM_REDUCTION,
mScreenBrightnessDimConfig), mScreenBrightnessRangeMinimum);
+ mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_DIMMED);
}
if (!mAppliedDimming) {
slowChange = false;
@@ -982,6 +1007,7 @@
Math.min(mPowerRequest.screenLowPowerBrightnessFactor, 1);
final int lowPowerBrightness = (int) (brightness * brightnessFactor);
brightness = Math.max(lowPowerBrightness, mScreenBrightnessRangeMinimum);
+ mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_LOW_POWER);
}
if (!mAppliedLowPower) {
slowChange = false;
@@ -1047,6 +1073,14 @@
}
+ // Log any changes to what is currently driving the brightness setting.
+ if (!mBrightnessReasonTemp.equals(mBrightnessReason) || brightnessAdjustmentFlags != 0) {
+ Slog.v(TAG, "Brightness [" + brightness + "] reason changing to: '"
+ + mBrightnessReasonTemp.toString(brightnessAdjustmentFlags)
+ + "', previous reason: '" + mBrightnessReason + "'.");
+ mBrightnessReason.set(mBrightnessReasonTemp);
+ }
+
// Update display white-balance.
if (mDisplayWhiteBalanceController != null) {
if (state == Display.STATE_ON && mDisplayWhiteBalanceSettings.isEnabled()) {
@@ -1737,6 +1771,7 @@
pw.println(" mPendingScreenBrightnessSetting=" + mPendingScreenBrightnessSetting);
pw.println(" mTemporaryScreenBrightness=" + mTemporaryScreenBrightness);
pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment);
+ pw.println(" mBrightnessReason=" + mBrightnessReason);
pw.println(" mTemporaryAutoBrightnessAdjustment=" + mTemporaryAutoBrightnessAdjustment);
pw.println(" mPendingAutoBrightnessAdjustment=" + mPendingAutoBrightnessAdjustment);
pw.println(" mScreenBrightnessForVr=" + mScreenBrightnessForVr);
@@ -1956,4 +1991,121 @@
sendUpdatePowerState();
}
}
+
+ /**
+ * Stores data about why the brightness was changed. Made up of one main
+ * {@code BrightnessReason.REASON_*} reason and various {@code BrightnessReason.MODIFIER_*}
+ * modifiers.
+ */
+ private final class BrightnessReason {
+ static final int REASON_UNKNOWN = 0;
+ static final int REASON_MANUAL = 1;
+ static final int REASON_DOZE = 2;
+ static final int REASON_DOZE_DEFAULT = 3;
+ static final int REASON_AUTOMATIC = 4;
+ static final int REASON_SCREEN_OFF = 5;
+ static final int REASON_VR = 6;
+ static final int REASON_OVERRIDE = 7;
+ static final int REASON_TEMPORARY = 8;
+ static final int REASON_BOOST = 9;
+ static final int REASON_MAX = REASON_BOOST;
+
+ static final int MODIFIER_DIMMED = 0x1;
+ static final int MODIFIER_LOW_POWER = 0x2;
+ static final int MODIFIER_MASK = 0x3;
+
+ // ADJUSTMENT_*
+ // These things can happen at any point, even if the main brightness reason doesn't
+ // fundamentally change, so they're not stored.
+
+ // Auto-brightness adjustment factor changed
+ static final int ADJUSTMENT_AUTO_TEMP = 0x1;
+ // Temporary adjustment to the auto-brightness adjustment factor.
+ static final int ADJUSTMENT_AUTO = 0x2;
+
+ // One of REASON_*
+ public int reason;
+ // Any number of MODIFIER_*
+ public int modifier;
+
+ public void set(BrightnessReason other) {
+ setReason(other == null ? REASON_UNKNOWN : other.reason);
+ setModifier(other == null ? 0 : other.modifier);
+ }
+
+ public void setReason(int reason) {
+ if (reason < REASON_UNKNOWN || reason > REASON_MAX) {
+ Slog.w(TAG, "brightness reason out of bounds: " + reason);
+ } else {
+ this.reason = reason;
+ }
+ }
+
+ public void setModifier(int modifier) {
+ if ((modifier & ~MODIFIER_MASK) != 0) {
+ Slog.w(TAG, "brightness modifier out of bounds: 0x"
+ + Integer.toHexString(modifier));
+ } else {
+ this.modifier = modifier;
+ }
+ }
+
+ public void addModifier(int modifier) {
+ setModifier(modifier | this.modifier);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof BrightnessReason)) {
+ return false;
+ }
+ BrightnessReason other = (BrightnessReason) obj;
+ return other.reason == reason && other.modifier == modifier;
+ }
+
+ @Override
+ public String toString() {
+ return toString(0);
+ }
+
+ public String toString(int adjustments) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(reasonToString(reason));
+ sb.append(" [");
+ if ((adjustments & ADJUSTMENT_AUTO_TEMP) != 0) {
+ sb.append(" temp_adj");
+ }
+ if ((adjustments & ADJUSTMENT_AUTO) != 0) {
+ sb.append(" auto_adj");
+ }
+ if ((modifier & MODIFIER_LOW_POWER) != 0) {
+ sb.append(" low_pwr");
+ }
+ if ((modifier & MODIFIER_DIMMED) != 0) {
+ sb.append(" dim");
+ }
+ int strlen = sb.length();
+ if (sb.charAt(strlen - 1) == '[') {
+ sb.setLength(strlen - 2);
+ } else {
+ sb.append(" ]");
+ }
+ return sb.toString();
+ }
+
+ private String reasonToString(int reason) {
+ switch (reason) {
+ case REASON_MANUAL: return "manual";
+ case REASON_DOZE: return "doze";
+ case REASON_DOZE_DEFAULT: return "doze_default";
+ case REASON_AUTOMATIC: return "automatic";
+ case REASON_SCREEN_OFF: return "screen_off";
+ case REASON_VR: return "vr";
+ case REASON_OVERRIDE: return "override";
+ case REASON_TEMPORARY: return "temporary";
+ case REASON_BOOST: return "boost";
+ default: return Integer.toString(reason);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index b2420b5..64a9e00 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -394,7 +394,9 @@
private void tearDown() {
Slog.d(TAG, "tearDown: currentUser=" + mCurrentUser);
- getContext().getContentResolver().unregisterContentObserver(mContentObserver);
+ if (mContentObserver != null) {
+ getContext().getContentResolver().unregisterContentObserver(mContentObserver);
+ }
if (mNightDisplayTintController.isAvailable(getContext())) {
if (mNightDisplayAutoMode != null) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index ea1c49d..ab4ae9d 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -1635,6 +1635,9 @@
*/
private static final long PERIODIC_JOB_WINDOW_BUFFER = 30 * MINUTE_IN_MILLIS;
+ /** The maximum period a periodic job can have. Anything higher will be clamped down to this. */
+ public static final long MAX_ALLOWED_PERIOD_MS = 365 * 24 * 60 * 60 * 1000L;
+
/**
* Called after a periodic has executed so we can reschedule it. We take the last execution
* time of the job to be the time of completion (i.e. the time at which this function is
@@ -1652,11 +1655,21 @@
JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
final long elapsedNow = sElapsedRealtimeClock.millis();
final long newLatestRuntimeElapsed;
- final long period = periodicToReschedule.getJob().getIntervalMillis();
- final long latestRunTimeElapsed = periodicToReschedule.getOriginalLatestRunTimeElapsed();
- final long flex = periodicToReschedule.getJob().getFlexMillis();
+ // Make sure period is in the interval [min_possible_period, max_possible_period].
+ final long period = Math.max(JobInfo.getMinPeriodMillis(),
+ Math.min(MAX_ALLOWED_PERIOD_MS, periodicToReschedule.getJob().getIntervalMillis()));
+ // Make sure flex is in the interval [min_possible_flex, period].
+ final long flex = Math.max(JobInfo.getMinFlexMillis(),
+ Math.min(period, periodicToReschedule.getJob().getFlexMillis()));
long rescheduleBuffer = 0;
+ long olrte = periodicToReschedule.getOriginalLatestRunTimeElapsed();
+ if (olrte < 0 || olrte == JobStatus.NO_LATEST_RUNTIME) {
+ Slog.wtf(TAG, "Invalid periodic job original latest run time: " + olrte);
+ olrte = elapsedNow;
+ }
+ final long latestRunTimeElapsed = olrte;
+
final long diffMs = Math.abs(elapsedNow - latestRunTimeElapsed);
if (elapsedNow > latestRunTimeElapsed) {
// The job ran past its expected run window. Have it count towards the current window
@@ -1664,7 +1677,7 @@
if (DEBUG) {
Slog.i(TAG, "Periodic job ran after its intended window.");
}
- int numSkippedWindows = (int) (diffMs / period) + 1; // +1 to include original window
+ long numSkippedWindows = (diffMs / period) + 1; // +1 to include original window
if (period != flex && diffMs > Math.min(PERIODIC_JOB_WINDOW_BUFFER,
(period - flex) / 2)) {
if (DEBUG) {
@@ -1684,6 +1697,16 @@
}
}
+ if (newLatestRuntimeElapsed < elapsedNow) {
+ Slog.wtf(TAG, "Rescheduling calculated latest runtime in the past: "
+ + newLatestRuntimeElapsed);
+ return new JobStatus(periodicToReschedule, getCurrentHeartbeat(),
+ elapsedNow + period - flex, elapsedNow + period,
+ 0 /* backoffAttempt */,
+ sSystemClock.millis() /* lastSuccessfulRunTime */,
+ periodicToReschedule.getLastFailedRunTime());
+ }
+
final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed
- Math.min(flex, period - rescheduleBuffer);
@@ -2280,8 +2303,6 @@
if (bucket >= mConstants.STANDBY_BEATS.length
|| (mHeartbeat > appLastRan
&& mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
- // TODO: log/trace that we're deferring the job due to bucketing if we
- // hit this
if (job.getWhenStandbyDeferred() == 0) {
if (DEBUG_STANDBY) {
Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index fd20e11..eb5d472 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -511,8 +511,13 @@
final long elapsedNow = sElapsedRealtimeClock.millis();
final long earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis;
if (job.isPeriodic()) {
- latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis();
- earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis - job.getFlexMillis();
+ // Make sure period is in the interval [min_possible_period, max_possible_period].
+ final long period = Math.max(JobInfo.getMinPeriodMillis(),
+ Math.min(JobSchedulerService.MAX_ALLOWED_PERIOD_MS, job.getIntervalMillis()));
+ latestRunTimeElapsedMillis = elapsedNow + period;
+ earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis
+ // Make sure flex is in the interval [min_possible_flex, period].
+ - Math.max(JobInfo.getMinFlexMillis(), Math.min(period, job.getFlexMillis()));
} else {
earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;
@@ -1631,6 +1636,9 @@
formatRunTime(pw, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, elapsedRealtimeMillis);
pw.print(", latest=");
formatRunTime(pw, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, elapsedRealtimeMillis);
+ pw.print(", original latest=");
+ formatRunTime(pw, mOriginalLatestRunTimeElapsedMillis,
+ NO_LATEST_RUNTIME, elapsedRealtimeMillis);
pw.println();
if (numFailures != 0) {
pw.print(prefix); pw.print("Num failures: "); pw.println(numFailures);
diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java
index ef6944e..18d193a 100644
--- a/services/core/java/com/android/server/job/controllers/QuotaController.java
+++ b/services/core/java/com/android/server/job/controllers/QuotaController.java
@@ -617,7 +617,7 @@
jobStatus.setTrackingController(JobStatus.TRACKING_QUOTA);
if (mShouldThrottle) {
final boolean isWithinQuota = isWithinQuotaLocked(jobStatus);
- jobStatus.setQuotaConstraintSatisfied(isWithinQuota);
+ setConstraintSatisfied(jobStatus, isWithinQuota);
if (!isWithinQuota) {
maybeScheduleStartAlarmLocked(userId, pkgName,
getEffectiveStandbyBucket(jobStatus));
@@ -1282,10 +1282,10 @@
// An app in the ACTIVE bucket may be out of quota while the job could be in quota
// for some reason. Therefore, avoid setting the real value here and check each job
// individually.
- changed |= js.setQuotaConstraintSatisfied(realInQuota);
+ changed |= setConstraintSatisfied(js, realInQuota);
} else {
// This job is somehow exempted. Need to determine its own quota status.
- changed |= js.setQuotaConstraintSatisfied(isWithinQuotaLocked(js));
+ changed |= setConstraintSatisfied(js, isWithinQuotaLocked(js));
}
}
if (!realInQuota) {
@@ -1310,7 +1310,7 @@
@Override
public void accept(JobStatus jobStatus) {
- wasJobChanged |= jobStatus.setQuotaConstraintSatisfied(isWithinQuotaLocked(jobStatus));
+ wasJobChanged |= setConstraintSatisfied(jobStatus, isWithinQuotaLocked(jobStatus));
final int userId = jobStatus.getSourceUserId();
final String packageName = jobStatus.getSourcePackageName();
final int realStandbyBucket = jobStatus.getStandbyBucket();
@@ -1434,6 +1434,14 @@
}
}
+ private boolean setConstraintSatisfied(@NonNull JobStatus jobStatus, boolean isWithinQuota) {
+ if (!isWithinQuota && jobStatus.getWhenStandbyDeferred() == 0) {
+ // Mark that the job is being deferred due to buckets.
+ jobStatus.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
+ }
+ return jobStatus.setQuotaConstraintSatisfied(isWithinQuota);
+ }
+
private final class ChargingTracker extends BroadcastReceiver {
/**
* Track whether we're charging. This has a slightly different definition than that of
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 19ff2c1..6c34e13 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -217,6 +217,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.os.RoSystemProperties;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ConcurrentUtils;
@@ -1353,9 +1354,15 @@
mContext, 0, snoozeIntent, PendingIntent.FLAG_UPDATE_CURRENT));
final Intent viewIntent = buildViewDataUsageIntent(res, policy.template);
- builder.setContentIntent(PendingIntent.getActivity(
- mContext, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT));
-
+ // TODO: Resolve to single code path.
+ if (isHeadlessSystemUserBuild()) {
+ builder.setContentIntent(PendingIntent.getActivityAsUser(
+ mContext, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT,
+ /* options= */ null, UserHandle.CURRENT));
+ } else {
+ builder.setContentIntent(PendingIntent.getActivity(
+ mContext, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT));
+ }
break;
}
case TYPE_LIMIT: {
@@ -1375,8 +1382,15 @@
builder.setSmallIcon(R.drawable.stat_notify_disabled_data);
final Intent intent = buildNetworkOverLimitIntent(res, policy.template);
- builder.setContentIntent(PendingIntent.getActivity(
- mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
+ // TODO: Resolve to single code path.
+ if (isHeadlessSystemUserBuild()) {
+ builder.setContentIntent(PendingIntent.getActivityAsUser(
+ mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT,
+ /* options= */ null, UserHandle.CURRENT));
+ } else {
+ builder.setContentIntent(PendingIntent.getActivity(
+ mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
+ }
break;
}
case TYPE_LIMIT_SNOOZED: {
@@ -1399,8 +1413,15 @@
builder.setChannelId(SystemNotificationChannels.NETWORK_STATUS);
final Intent intent = buildViewDataUsageIntent(res, policy.template);
- builder.setContentIntent(PendingIntent.getActivity(
- mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
+ // TODO: Resolve to single code path.
+ if (isHeadlessSystemUserBuild()) {
+ builder.setContentIntent(PendingIntent.getActivityAsUser(
+ mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT,
+ /* options= */ null, UserHandle.CURRENT));
+ } else {
+ builder.setContentIntent(PendingIntent.getActivity(
+ mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
+ }
break;
}
case TYPE_RAPID: {
@@ -1419,8 +1440,15 @@
mContext, 0, snoozeIntent, PendingIntent.FLAG_UPDATE_CURRENT));
final Intent viewIntent = buildViewDataUsageIntent(res, policy.template);
- builder.setContentIntent(PendingIntent.getActivity(
- mContext, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT));
+ // TODO: Resolve to single code path.
+ if (isHeadlessSystemUserBuild()) {
+ builder.setContentIntent(PendingIntent.getActivityAsUser(
+ mContext, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT,
+ /* options= */ null, UserHandle.CURRENT));
+ } else {
+ builder.setContentIntent(PendingIntent.getActivity(
+ mContext, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT));
+ }
break;
}
default: {
@@ -5264,6 +5292,10 @@
return (bundle != null) ? bundle.getBoolean(key, defaultValue) : defaultValue;
}
+ private static boolean isHeadlessSystemUserBuild() {
+ return RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER;
+ }
+
private class NotificationId {
private final String mTag;
private final int mId;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c4eb661..8485f46 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -283,7 +283,7 @@
static final boolean DEBUG_INTERRUPTIVENESS = SystemProperties.getBoolean(
"debug.notification.interruptiveness", false);
- static final int MAX_PACKAGE_NOTIFICATIONS = 50;
+ static final int MAX_PACKAGE_NOTIFICATIONS = 25;
static final float DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE = 5f;
// message codes
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 1c0ac16..7b45a1b 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -78,7 +78,7 @@
private static final String TAG_CHANNEL = "channel";
private static final String TAG_GROUP = "channelGroup";
private static final String TAG_DELEGATE = "delegate";
- private static final String TAG_STATUS_ICONS = "status_icons";
+ private static final String TAG_STATUS_ICONS = "silent_status_icons";
private static final String ATT_VERSION = "version";
private static final String ATT_NAME = "name";
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2f86632..5958715 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2578,9 +2578,7 @@
mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N;
mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;
- mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q
- // STOPSHIP: Remove next line when API level for Q is defined.
- && Build.VERSION.SDK_INT > Build.VERSION_CODES.P;
+ mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q;
int preUpgradeSdkVersion = ver.sdkVersion;
@@ -14757,7 +14755,7 @@
if (ps != null) {
try {
- rm.restoreUserData(packageName, installedUsers, appId, ceDataInode,
+ rm.snapshotAndRestoreUserData(packageName, installedUsers, appId, ceDataInode,
seInfo, token);
} catch (RemoteException re) {
// Cannot happen, the RollbackManager is hosted in the same process.
@@ -15565,6 +15563,22 @@
@Override
void handleReturnCode() {
if (mVerificationCompleted && mEnableRollbackCompleted) {
+ if ((installFlags & PackageManager.INSTALL_DRY_RUN) != 0) {
+ String packageName = "";
+ try {
+ PackageLite packageInfo =
+ new PackageParser().parsePackageLite(origin.file, 0);
+ packageName = packageInfo.packageName;
+ } catch (PackageParserException e) {
+ Slog.e(TAG, "Can't parse package at " + origin.file.getAbsolutePath(), e);
+ }
+ try {
+ observer.onPackageInstalled(packageName, mRet, "Dry run", new Bundle());
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Observer no longer exists.");
+ }
+ return;
+ }
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
mRet = mArgs.copyApk();
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 950450c..9c87c74 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -57,6 +57,7 @@
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
@@ -201,10 +202,6 @@
private void preRebootVerification(@NonNull PackageInstallerSession session) {
boolean success = true;
- // STOPSHIP: TODO(b/123753157): Verify APKs through Package Verifier.
- // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs,
- // right away.
-
final ApexInfoList apexInfoList = new ApexInfoList();
// APEX checks. For single-package sessions, check if they contain an APEX. For
// multi-package sessions, find all the child sessions that contain an APEX.
@@ -230,6 +227,16 @@
return;
}
+ if (sessionContainsApk(session)) {
+ if (!installApksInSession(session, /* preReboot */ true)) {
+ session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
+ "APK verification failed. Check logcat messages for "
+ + "more information.");
+ // TODO(b/118865310): abort the session on apexd.
+ return;
+ }
+ }
+
if (apexInfoList.apexInfos != null && apexInfoList.apexInfos.length > 0) {
// For APEXes, we validate the signature here before we mark the session as ready,
// so we fail the session early if there is a signature mismatch. For APKs, the
@@ -275,21 +282,31 @@
}
}
- private boolean sessionContainsApex(@NonNull PackageInstallerSession session) {
+
+ private boolean sessionContains(@NonNull PackageInstallerSession session,
+ Predicate<PackageInstallerSession> filter) {
if (!session.isMultiPackage()) {
- return isApexSession(session);
+ return filter.test(session);
}
synchronized (mStagedSessions) {
return !(Arrays.stream(session.getChildSessionIds())
// Retrieve cached sessions matching ids.
.mapToObj(i -> mStagedSessions.get(i))
// Filter only the ones containing APEX.
- .filter(childSession -> isApexSession(childSession))
+ .filter(childSession -> filter.test(childSession))
.collect(Collectors.toList())
.isEmpty());
}
}
+ private boolean sessionContainsApex(@NonNull PackageInstallerSession session) {
+ return sessionContains(session, (s) -> isApexSession(s));
+ }
+
+ private boolean sessionContainsApk(@NonNull PackageInstallerSession session) {
+ return sessionContains(session, (s) -> !isApexSession(s));
+ }
+
private void resumeSession(@NonNull PackageInstallerSession session) {
boolean hasApex = sessionContainsApex(session);
if (hasApex) {
@@ -326,7 +343,7 @@
}
}
// The APEX part of the session is activated, proceed with the installation of APKs.
- if (!installApksInSession(session)) {
+ if (!installApksInSession(session, /* preReboot */ false)) {
session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
"Staged installation of APKs failed. Check logcat messages for"
+ "more information.");
@@ -343,7 +360,6 @@
+ "to the previous state of APEXd.");
mPowerManager.reboot(null);
}
-
return;
}
@@ -366,7 +382,7 @@
}
private PackageInstallerSession createAndWriteApkSession(
- @NonNull PackageInstallerSession originalSession) {
+ @NonNull PackageInstallerSession originalSession, boolean preReboot) {
if (originalSession.stageDir == null) {
Slog.wtf(TAG, "Attempting to install a staged APK session with no staging dir");
return null;
@@ -379,9 +395,14 @@
PackageInstaller.SessionParams params = originalSession.params.copy();
params.isStaged = false;
- params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION;
params.installFlags |= PackageManager.INSTALL_STAGED;
// TODO(b/129744602): use the userid from the original session.
+ if (preReboot) {
+ params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
+ params.installFlags |= PackageManager.INSTALL_DRY_RUN;
+ } else {
+ params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION;
+ }
int apkSessionId = mPi.createSession(
params, originalSession.getInstallerPackageName(),
0 /* UserHandle.SYSTEM */);
@@ -408,17 +429,19 @@
}
private boolean commitApkSession(@NonNull PackageInstallerSession apkSession,
- int originalSessionId) {
+ int originalSessionId, boolean preReboot) {
- if ((apkSession.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
- // If rollback is available for this session, notify the rollback
- // manager of the apk session so it can properly enable rollback.
- final IRollbackManager rm = IRollbackManager.Stub.asInterface(
- ServiceManager.getService(Context.ROLLBACK_SERVICE));
- try {
- rm.notifyStagedApkSession(originalSessionId, apkSession.sessionId);
- } catch (RemoteException re) {
- // Cannot happen, the rollback manager is in the same process.
+ if (!preReboot) {
+ if ((apkSession.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
+ // If rollback is available for this session, notify the rollback
+ // manager of the apk session so it can properly enable rollback.
+ final IRollbackManager rm = IRollbackManager.Stub.asInterface(
+ ServiceManager.getService(Context.ROLLBACK_SERVICE));
+ try {
+ rm.notifyStagedApkSession(originalSessionId, apkSession.sessionId);
+ } catch (RemoteException re) {
+ // Cannot happen, the rollback manager is in the same process.
+ }
}
}
@@ -435,14 +458,15 @@
return false;
}
- private boolean installApksInSession(@NonNull PackageInstallerSession session) {
+ private boolean installApksInSession(@NonNull PackageInstallerSession session,
+ boolean preReboot) {
if (!session.isMultiPackage() && !isApexSession(session)) {
// APK single-packaged staged session. Do a regular install.
- PackageInstallerSession apkSession = createAndWriteApkSession(session);
+ PackageInstallerSession apkSession = createAndWriteApkSession(session, preReboot);
if (apkSession == null) {
return false;
}
- return commitApkSession(apkSession, session.sessionId);
+ return commitApkSession(apkSession, session.sessionId, preReboot);
} else if (session.isMultiPackage()) {
// For multi-package staged sessions containing APKs, we identify which child sessions
// contain an APK, and with those then create a new multi-package group of sessions,
@@ -464,6 +488,9 @@
}
PackageInstaller.SessionParams params = session.params.copy();
params.isStaged = false;
+ if (preReboot) {
+ params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
+ }
// TODO(b/129744602): use the userid from the original session.
int apkParentSessionId = mPi.createSession(
params, session.getInstallerPackageName(),
@@ -478,7 +505,8 @@
}
for (PackageInstallerSession sessionToClone : childSessions) {
- PackageInstallerSession apkChildSession = createAndWriteApkSession(sessionToClone);
+ PackageInstallerSession apkChildSession =
+ createAndWriteApkSession(sessionToClone, preReboot);
if (apkChildSession == null) {
return false;
}
@@ -489,7 +517,7 @@
return false;
}
}
- return commitApkSession(apkParentSession, session.sessionId);
+ return commitApkSession(apkParentSession, session.sessionId, preReboot);
}
// APEX single-package staged session, nothing to do.
return true;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 267fbf0..4edd9ef 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -97,6 +97,7 @@
import com.android.server.pm.UserManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback;
import com.android.server.pm.permission.PermissionsState.PermissionState;
+import com.android.server.policy.SoftRestrictedPermissionPolicy;
import libcore.util.EmptyArray;
@@ -1173,6 +1174,14 @@
}
}
+ if (hardRestricted && !restrictionExempt
+ && (flags & FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
+ // Applying a hard restriction implies revoking it. This might
+ // lead to a system-fixed, revoked permission.
+ flags &= ~FLAG_PERMISSION_SYSTEM_FIXED;
+ wasChanged = true;
+ }
+
if (wasChanged) {
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
}
@@ -2113,11 +2122,18 @@
if (bp.isHardRestricted()
&& (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) {
- Log.e(TAG, "Cannot grant restricted non-exempt permission "
+ Log.e(TAG, "Cannot grant hard restricted non-exempt permission "
+ permName + " for package " + packageName);
return;
}
+ if (bp.isSoftRestricted() && !SoftRestrictedPermissionPolicy.forPermission(mContext,
+ pkg.applicationInfo, permName).canBeGranted()) {
+ Log.e(TAG, "Cannot grant soft restricted permission " + permName + " for package "
+ + packageName);
+ return;
+ }
+
if (bp.isDevelopment()) {
// Development permissions must be handled specially, since they are not
// normal runtime permissions. For now they apply to all users.
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index a799cd9..1d01a84 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -16,10 +16,14 @@
package com.android.server.policy;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_ERRORED;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OP_NONE;
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
-import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -426,20 +430,34 @@
mOpsToAllowIfDefault.add(new OpToUnrestrict(uid, pkg.packageName, opCode));
}
} else if (permissionInfo.isSoftRestricted()) {
- // Storage uses a special app op to decide the mount state and
- // supports soft restriction where the restricted state allows
- // the permission but only for accessing the medial collections.
- if (Manifest.permission.READ_EXTERNAL_STORAGE.equals(permission)
- || Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission)) {
- if (applyRestriction) {
- mOpsToDefault.add(new OpToRestrict(uid,
- AppOpsManager.OP_LEGACY_STORAGE));
- } else if (pkg.applicationInfo.hasRequestedLegacyExternalStorage()) {
- mOpsToAllow.add(new OpToUnrestrict(uid, pkg.packageName,
- AppOpsManager.OP_LEGACY_STORAGE));
- } else {
- mOpsToIgnoreIfDefault.add(new OpToUnrestrict(uid, pkg.packageName,
- AppOpsManager.OP_LEGACY_STORAGE));
+ final SoftRestrictedPermissionPolicy policy =
+ SoftRestrictedPermissionPolicy.forPermission(mContext, pkg.applicationInfo,
+ permission);
+
+ final int op = policy.getAppOp();
+ if (op != OP_NONE) {
+ switch (policy.getAppOpMode()) {
+ case MODE_DEFAULT:
+ mOpsToDefault.add(new OpToRestrict(uid, op));
+ break;
+ case MODE_ALLOWED:
+ if (policy.shouldSetAppOpIfNotDefault()) {
+ mOpsToAllow.add(new OpToUnrestrict(uid, pkg.packageName, op));
+ } else {
+ mOpsToAllowIfDefault.add(new OpToUnrestrict(uid, pkg.packageName,
+ op));
+ }
+ break;
+ case MODE_IGNORED:
+ if (policy.shouldSetAppOpIfNotDefault()) {
+ Slog.wtf(LOG_TAG, "Always ignoring appops is not implemented");
+ } else {
+ mOpsToIgnoreIfDefault.add(new OpToUnrestrict(uid, pkg.packageName,
+ op));
+ }
+ break;
+ case MODE_ERRORED:
+ Slog.wtf(LOG_TAG, "Setting appop to errored is not implemented");
}
}
}
@@ -483,7 +501,7 @@
for (String permission : pkg.requestedPermissions) {
final int opCode = AppOpsManager.permissionToOpCode(permission);
- if (opCode == AppOpsManager.OP_NONE) {
+ if (opCode == OP_NONE) {
continue;
}
@@ -515,13 +533,13 @@
@NonNull String packageName) {
final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager
.opToPublicName(opCode), uid, packageName);
- if (currentMode == AppOpsManager.MODE_DEFAULT) {
+ if (currentMode == MODE_DEFAULT) {
mAppOpsManager.setUidMode(opCode, uid, mode);
}
}
private void setUidModeDefault(int opCode, int uid) {
- mAppOpsManager.setUidMode(opCode, uid, AppOpsManager.MODE_DEFAULT);
+ mAppOpsManager.setUidMode(opCode, uid, MODE_DEFAULT);
}
private class OpToRestrict {
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
new file mode 100644
index 0000000..e19b708
--- /dev/null
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.policy;
+
+import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
+import static android.app.AppOpsManager.OP_NONE;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+
+import android.annotation.NonNull;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.Build;
+import android.util.Log;
+
+/**
+ * The behavior of soft restricted permissions is different for each permission. This class collects
+ * the policies in one place.
+ *
+ * This is the twin of
+ * {@link com.android.packageinstaller.permission.utils.SoftRestrictedPermissionPolicy}
+ */
+public abstract class SoftRestrictedPermissionPolicy {
+ private static final String LOG_TAG = SoftRestrictedPermissionPolicy.class.getSimpleName();
+
+ private static final int FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT =
+ FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
+ | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT
+ | FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+
+ private static final SoftRestrictedPermissionPolicy DUMMY_POLICY =
+ new SoftRestrictedPermissionPolicy() {
+ @Override
+ public int getAppOp() {
+ return OP_NONE;
+ }
+
+ @Override
+ public int getAppOpMode() {
+ return MODE_DEFAULT;
+ }
+
+ @Override
+ public boolean shouldSetAppOpIfNotDefault() {
+ return false;
+ }
+
+ @Override
+ public boolean canBeGranted() {
+ return true;
+ }
+ };
+
+ /**
+ * Get the policy for a soft restricted permission.
+ *
+ * @param context A context to use
+ * @param appInfo The application the permission belongs to
+ * @param permission The name of the permission
+ *
+ * @return The policy for this permission
+ */
+ public static @NonNull SoftRestrictedPermissionPolicy forPermission(@NonNull Context context,
+ @NonNull ApplicationInfo appInfo, @NonNull String permission) {
+ switch (permission) {
+ // Storage uses a special app op to decide the mount state and supports soft restriction
+ // where the restricted state allows the permission but only for accessing the medial
+ // collections.
+ case READ_EXTERNAL_STORAGE:
+ case WRITE_EXTERNAL_STORAGE: {
+ int flags = context.getPackageManager().getPermissionFlags(
+ permission, appInfo.packageName, context.getUser());
+ boolean applyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
+ boolean isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
+ boolean hasRequestedLegacyExternalStorage =
+ appInfo.hasRequestedLegacyExternalStorage();
+ int targetSDK = appInfo.targetSdkVersion;
+
+ return new SoftRestrictedPermissionPolicy() {
+ @Override
+ public int getAppOp() {
+ return OP_LEGACY_STORAGE;
+ }
+
+ @Override
+ public int getAppOpMode() {
+ if (applyRestriction) {
+ return MODE_DEFAULT;
+ } else if (hasRequestedLegacyExternalStorage) {
+ return MODE_ALLOWED;
+ } else {
+ return MODE_IGNORED;
+ }
+ }
+
+ @Override
+ public boolean shouldSetAppOpIfNotDefault() {
+ // Do not switch from allowed -> ignored as this would mean to retroactively
+ // turn on isolated storage. This will make the app loose all its files.
+ return getAppOpMode() != MODE_IGNORED;
+ }
+
+ @Override
+ public boolean canBeGranted() {
+ if (isWhiteListed || targetSDK >= Build.VERSION_CODES.Q) {
+ return true;
+ } else {
+ Log.w(LOG_TAG, permission + " for " + appInfo.packageName
+ + " is not whitelisted and targetSDK " + targetSDK + "<"
+ + Build.VERSION_CODES.Q);
+
+ return false;
+ }
+ }
+ };
+ }
+ default:
+ return DUMMY_POLICY;
+ }
+ }
+
+ /**
+ * @return An app op to be changed based on the state of the permission or
+ * {@link AppOpsManager#OP_NONE} if not app-op should be set.
+ */
+ public abstract int getAppOp();
+
+ /**
+ * @return The mode the {@link #getAppOp() app op} should be in.
+ */
+ public abstract @AppOpsManager.Mode int getAppOpMode();
+
+ /**
+ * @return If the {@link #getAppOp() app op} should be set even if the app-op is currently not
+ * {@link AppOpsManager#MODE_DEFAULT}.
+ */
+ public abstract boolean shouldSetAppOpIfNotDefault();
+
+ /**
+ * @return If the permission can be granted
+ */
+ public abstract boolean canBeGranted();
+}
diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING
index 437ef73..02b0e21 100644
--- a/services/core/java/com/android/server/policy/TEST_MAPPING
+++ b/services/core/java/com/android/server/policy/TEST_MAPPING
@@ -27,6 +27,14 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "CtsPermission2TestCases",
+ "options": [
+ {
+ "include-filter": "android.permission2.cts.RestrictedPermissionsTest"
+ }
+ ]
}
],
"postsubmit": [
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 9a2778d..301f650 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -923,8 +923,8 @@
}
if (rd != null) {
- // This is the apk session for a staged session. We have already
- // backed up the apks, we just need to do user data backup.
+ // This is the apk session for a staged session. We do not need to create a new rollback
+ // for this session.
PackageParser.PackageLite newPackage = null;
try {
newPackage = PackageParser.parsePackageLite(
@@ -937,8 +937,6 @@
for (PackageRollbackInfo info : rd.info.getPackages()) {
if (info.getPackageName().equals(packageName)) {
info.getInstalledUsers().addAll(IntArray.wrap(installedUsers));
- mAppDataRollbackHelper.snapshotAppData(rd.info.getRollbackId(), info);
- saveRollbackData(rd);
return true;
}
}
@@ -959,8 +957,7 @@
}
newRollback.addToken(token);
- return enableRollbackForPackageSession(newRollback.data, packageSession,
- installedUsers, /* snapshotUserData*/ true);
+ return enableRollbackForPackageSession(newRollback.data, packageSession, installedUsers);
}
/**
@@ -971,8 +968,7 @@
* @return true on success, false on failure.
*/
private boolean enableRollbackForPackageSession(RollbackData data,
- PackageInstaller.SessionInfo session, @NonNull int[] installedUsers,
- boolean snapshotUserData) {
+ PackageInstaller.SessionInfo session, @NonNull int[] installedUsers) {
// TODO: Don't attempt to enable rollback for split installs.
final int installFlags = session.installFlags;
if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) {
@@ -1033,10 +1029,6 @@
isApex, IntArray.wrap(installedUsers),
new SparseLongArray() /* ceSnapshotInodes */);
- if (snapshotUserData && !isApex) {
- mAppDataRollbackHelper.snapshotAppData(data.info.getRollbackId(), packageRollbackInfo);
- }
-
try {
ApplicationInfo appInfo = pkgInfo.applicationInfo;
RollbackStore.backupPackageCodePath(data, packageName, appInfo.sourceDir);
@@ -1057,13 +1049,15 @@
}
@Override
- public void restoreUserData(String packageName, int[] userIds, int appId, long ceDataInode,
- String seInfo, int token) {
+ public void snapshotAndRestoreUserData(String packageName, int[] userIds, int appId,
+ long ceDataInode, String seInfo, int token) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("restoreUserData may only be called by the system.");
+ throw new SecurityException(
+ "snapshotAndRestoreUserData may only be called by the system.");
}
getHandler().post(() -> {
+ snapshotUserDataInternal(packageName);
restoreUserDataInternal(packageName, userIds, appId, ceDataInode, seInfo, token);
final PackageManagerInternal pmi = LocalServices.getService(
PackageManagerInternal.class);
@@ -1071,6 +1065,38 @@
});
}
+ private void snapshotUserDataInternal(String packageName) {
+ synchronized (mLock) {
+ // staged installs
+ ensureRollbackDataLoadedLocked();
+ for (int i = 0; i < mRollbacks.size(); i++) {
+ RollbackData data = mRollbacks.get(i);
+ if (data.state != RollbackData.ROLLBACK_STATE_ENABLING) {
+ continue;
+ }
+
+ for (PackageRollbackInfo info : data.info.getPackages()) {
+ if (info.getPackageName().equals(packageName)) {
+ mAppDataRollbackHelper.snapshotAppData(data.info.getRollbackId(), info);
+ saveRollbackData(data);
+ return;
+ }
+ }
+ }
+ // non-staged installs
+ PackageRollbackInfo info;
+ for (NewRollback rollback : mNewRollbacks) {
+ info = getPackageRollbackInfo(rollback.data, packageName);
+ if (info != null) {
+ mAppDataRollbackHelper.snapshotAppData(rollback.data.info.getRollbackId(),
+ info);
+ saveRollbackData(rollback.data);
+ return;
+ }
+ }
+ }
+ }
+
private void restoreUserDataInternal(String packageName, int[] userIds, int appId,
long ceDataInode, String seInfo, int token) {
PackageRollbackInfo info = null;
@@ -1130,7 +1156,7 @@
if (!session.isMultiPackage()) {
if (!enableRollbackForPackageSession(newRollback.data, session,
- new int[0], /* snapshotUserData */ false)) {
+ new int[0])) {
Log.e(TAG, "Unable to enable rollback for session: " + sessionId);
result.offer(false);
return;
@@ -1145,7 +1171,7 @@
return;
}
if (!enableRollbackForPackageSession(newRollback.data, childSession,
- new int[0], /* snapshotUserData */ false)) {
+ new int[0])) {
Log.e(TAG, "Unable to enable rollback for session: " + sessionId);
result.offer(false);
return;
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index b10b5ac..63439d5 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -489,8 +489,7 @@
// Add in all the apps for every user/profile.
for (UserInfo profile : users) {
List<PackageInfo> pi =
- pm.getInstalledPackagesAsUser(
- PackageManager.MATCH_KNOWN_PACKAGES | PackageManager.MATCH_APEX,
+ pm.getInstalledPackagesAsUser(PackageManager.MATCH_KNOWN_PACKAGES,
profile.id);
for (int j = 0; j < pi.size(); j++) {
if (pi.get(j).applicationInfo != null) {
@@ -2513,7 +2512,7 @@
@Override
public void onBootPhase(int phase) {
super.onBootPhase(phase);
- if (phase == PHASE_BOOT_COMPLETED) {
+ if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
mStatsCompanionService.systemReady();
}
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 112104d..828f790 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -719,21 +719,6 @@
// Ensure state for the current user is applied, even if passed a non-current user.
final int net1 = gatherDisableActionsLocked(mCurrentUserId, 1);
final int net2 = gatherDisableActionsLocked(mCurrentUserId, 2);
-
- // TODO(b/113914868): investigation log for disappearing home button
- if (whichFlag == 1 && pkg != null && pkg.contains("systemui")) {
- String disabledData = "{ ";
- for (int i = 0; i < mDisableRecords.size(); i++) {
- DisableRecord tok = mDisableRecords.get(i);
- disabledData += " ([" + i + "] " + tok + "), ";
- }
- disabledData += " }";
- final UiState state = getUiState(displayId);
-
- Log.d(TAG, "disabledlocked (b/113914868): displayId=" + displayId + ", net1=" + net1
- + ", mDisabled1=" + state.mDisabled1 + ", token=" + token
- + ", mDisableRecords=" + mDisableRecords.size() + " => " + disabledData);
- }
final UiState state = getUiState(displayId);
if (!state.disableEquals(net1, net2)) {
state.setDisabled(net1, net2);
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java
index 2f50fcb..94a1baa 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerInternal.java
@@ -43,14 +43,14 @@
int callingUid, String targetPkg, Uri uri, int modeFlags, int userId);
NeededUriGrants checkGrantUriPermissionFromIntent(int callingUid,
String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId);
+ NeededUriGrants checkGrantUriPermissionFromIntent(int callingUid,
+ Intent intent, String targetPkg, int targetUserId);
/**
* Grant Uri permissions from one app to another. This method only extends
* permission grants if {@code callingUid} has permission to them.
*/
void grantUriPermissionFromIntent(int callingUid,
String targetPkg, Intent intent, int targetUserId);
- void grantUriPermissionFromIntent(int callingUid,
- String targetPkg, Intent intent, UriPermissionOwner owner, int targetUserId);
void grantUriPermissionUncheckedFromIntent(
NeededUriGrants needed, UriPermissionOwner owner);
IBinder newUriPermissionOwner(String name);
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 8b332d2..04f7c7e 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -1362,20 +1362,21 @@
}
@Override
- public void grantUriPermissionFromIntent(int callingUid, String targetPkg, Intent intent,
- int targetUserId) {
+ public NeededUriGrants checkGrantUriPermissionFromIntent(int callingUid, Intent intent,
+ String targetPkg, int targetUserId) {
synchronized (mLock) {
- UriGrantsManagerService.this.grantUriPermissionFromIntent(
- callingUid, targetPkg, intent, null, targetUserId);
+ final int mode = (intent != null) ? intent.getFlags() : 0;
+ return UriGrantsManagerService.this.checkGrantUriPermissionFromIntent(
+ callingUid, targetPkg, intent, mode, null, targetUserId);
}
}
@Override
public void grantUriPermissionFromIntent(int callingUid, String targetPkg, Intent intent,
- UriPermissionOwner owner, int targetUserId) {
+ int targetUserId) {
synchronized (mLock) {
UriGrantsManagerService.this.grantUriPermissionFromIntent(
- callingUid, targetPkg, intent, owner, targetUserId);
+ callingUid, targetPkg, intent, null, targetUserId);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 1344727..7c4b571 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -215,6 +215,7 @@
import com.android.server.AttributeCache.Entry;
import com.android.server.am.AppTimeTracker;
import com.android.server.am.PendingIntentRecord;
+import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriPermissionOwner;
import com.android.server.wm.ActivityMetricsLogger.WindowingModeTransitionInfoSnapshot;
import com.android.server.wm.ActivityStack.ActivityState;
@@ -1599,10 +1600,11 @@
* Deliver a new Intent to an existing activity, so that its onNewIntent()
* method will be called at the proper time.
*/
- final void deliverNewIntentLocked(int callingUid, Intent intent, String referrer) {
+ final void deliverNewIntentLocked(int callingUid, Intent intent, NeededUriGrants intentGrants,
+ String referrer) {
// The activity now gets access to the data associated with this Intent.
- mAtmService.mUgmInternal.grantUriPermissionFromIntent(callingUid, packageName,
- intent, getUriPermissionsLocked(), mUserId);
+ mAtmService.mUgmInternal.grantUriPermissionUncheckedFromIntent(intentGrants,
+ getUriPermissionsLocked());
final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
boolean unsent = true;
final boolean isTopActivityWhileSleeping = isTopRunningActivity() && isSleeping();
diff --git a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
index ad46248..c56a9e2 100644
--- a/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
+++ b/services/core/java/com/android/server/wm/ActivityServiceConnectionsHolder.java
@@ -101,7 +101,13 @@
if (mConnections == null || mConnections.isEmpty()) {
return;
}
- mService.mH.post(() -> mService.mAmInternal.disconnectActivityFromServices(this));
+ // Capture and null out mConnections, to guarantee that we process
+ // disconnect of these specific connections exactly once even if
+ // we're racing with rapid activity lifecycle churn and this
+ // method is invoked more than once on this object.
+ final Object disc = mConnections;
+ mConnections = null;
+ mService.mH.post(() -> mService.mAmInternal.disconnectActivityFromServices(this, disc));
}
public void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index b3b6efe..94c4a61 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -163,6 +163,7 @@
import com.android.server.am.AppTimeTracker;
import com.android.server.am.EventLogTags;
import com.android.server.am.PendingIntentRecord;
+import com.android.server.uri.NeededUriGrants;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -2781,7 +2782,7 @@
if (DEBUG_STATES) Slog.d(TAG_STATES,
"no-history finish of " + mLastNoHistoryActivity + " on new resume");
requestFinishActivityLocked(mLastNoHistoryActivity.appToken, Activity.RESULT_CANCELED,
- null, "resume-no-history", false);
+ null, null, "resume-no-history", false);
mLastNoHistoryActivity = null;
}
@@ -3015,7 +3016,7 @@
// If any exception gets thrown, toss away this
// activity and try the next one.
Slog.w(TAG, "Exception thrown during resume of " + next, e);
- requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
+ requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null, null,
"resume-exception", true);
return true;
}
@@ -3425,7 +3426,7 @@
if (DEBUG_TASKS) Slog.w(TAG_TASKS,
"resetTaskIntendedTask: calling finishActivity on " + p);
if (finishActivityLocked(
- p, Activity.RESULT_CANCELED, null, "reset-task", false)) {
+ p, Activity.RESULT_CANCELED, null, null, "reset-task", false)) {
end--;
srcPos--;
}
@@ -3504,7 +3505,7 @@
continue;
}
finishActivityLocked(
- p, Activity.RESULT_CANCELED, null, "move-affinity", false);
+ p, Activity.RESULT_CANCELED, null, null, "move-affinity", false);
}
} else {
if (taskInsertionPoint < 0) {
@@ -3538,8 +3539,8 @@
if (targetNdx > 0) {
ActivityRecord p = taskActivities.get(targetNdx - 1);
if (p.intent.getComponent().equals(target.intent.getComponent())) {
- finishActivityLocked(p, Activity.RESULT_CANCELED, null, "replace",
- false);
+ finishActivityLocked(p, Activity.RESULT_CANCELED, null, null,
+ "replace", false);
}
}
}
@@ -3599,22 +3600,21 @@
return taskTop;
}
- void sendActivityResultLocked(int callingUid, ActivityRecord r,
- String resultWho, int requestCode, int resultCode, Intent data) {
-
+ void sendActivityResultLocked(int callingUid, ActivityRecord r, String resultWho,
+ int requestCode, int resultCode, Intent resultData, NeededUriGrants resultGrants) {
if (callingUid > 0) {
- mService.mUgmInternal.grantUriPermissionFromIntent(callingUid, r.packageName,
- data, r.getUriPermissionsLocked(), r.mUserId);
+ mService.mUgmInternal.grantUriPermissionUncheckedFromIntent(resultGrants,
+ r.getUriPermissionsLocked());
}
if (DEBUG_RESULTS) Slog.v(TAG, "Send activity result to " + r
+ " : who=" + resultWho + " req=" + requestCode
- + " res=" + resultCode + " data=" + data);
+ + " res=" + resultCode + " data=" + resultData);
if (mResumedActivity == r && r.attachedToProcess()) {
try {
ArrayList<ResultInfo> list = new ArrayList<ResultInfo>();
list.add(new ResultInfo(resultWho, requestCode,
- resultCode, data));
+ resultCode, resultData));
mService.getLifecycleManager().scheduleTransaction(r.app.getThread(), r.appToken,
ActivityResultItem.obtain(list));
return;
@@ -3623,7 +3623,7 @@
}
}
- r.addResultLocked(null, resultWho, requestCode, resultCode, data);
+ r.addResultLocked(null, resultWho, requestCode, resultCode, resultData);
}
/** Returns true if the task is one of the task finishing on-top of the top running task. */
@@ -3730,8 +3730,8 @@
if (!r.finishing) {
if (!shouldSleepActivities()) {
if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + r);
- if (requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
- "stop-no-history", false)) {
+ if (requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED,
+ null, null, "stop-no-history", false)) {
// If {@link requestFinishActivityLocked} returns {@code true},
// {@link adjustFocusedActivityStack} would have been already called.
r.resumeKeyDispatchingLocked();
@@ -3787,7 +3787,7 @@
* some reason it is being left as-is.
*/
final boolean requestFinishActivityLocked(IBinder token, int resultCode,
- Intent resultData, String reason, boolean oomAdj) {
+ Intent resultData, NeededUriGrants resultGrants, String reason, boolean oomAdj) {
ActivityRecord r = isInStackLocked(token);
if (DEBUG_RESULTS || DEBUG_STATES) Slog.v(TAG_STATES,
"Finishing activity token=" + token + " r="
@@ -3797,7 +3797,7 @@
return false;
}
- finishActivityLocked(r, resultCode, resultData, reason, oomAdj);
+ finishActivityLocked(r, resultCode, resultData, resultGrants, reason, oomAdj);
return true;
}
@@ -3809,8 +3809,8 @@
if (r.resultTo == self && r.requestCode == requestCode) {
if ((r.resultWho == null && resultWho == null) ||
(r.resultWho != null && r.resultWho.equals(resultWho))) {
- finishActivityLocked(r, Activity.RESULT_CANCELED, null, "request-sub",
- false);
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, null,
+ "request-sub", false);
}
}
}
@@ -3840,7 +3840,7 @@
int activityNdx = task.mActivities.indexOf(r);
getDisplay().mDisplayContent.prepareAppTransition(
TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
- finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, null, reason, false);
finishedTask = task;
// Also terminate any activities below it that aren't yet
// stopped, to avoid a situation where one will get
@@ -3861,7 +3861,7 @@
if (!r.isActivityTypeHome() || mService.mHomeProcess != r.app) {
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
- finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false);
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, null, reason, false);
}
}
}
@@ -3877,8 +3877,8 @@
for (int activityNdx = tr.mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord r = tr.mActivities.get(activityNdx);
if (!r.finishing) {
- finishActivityLocked(r, Activity.RESULT_CANCELED, null, "finish-voice",
- false);
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, null,
+ "finish-voice", false);
didOne = true;
}
}
@@ -3914,12 +3914,14 @@
if (!Objects.equals(cur.taskAffinity, r.taskAffinity)) {
break;
}
- finishActivityLocked(cur, Activity.RESULT_CANCELED, null, "request-affinity", true);
+ finishActivityLocked(cur, Activity.RESULT_CANCELED, null, null,
+ "request-affinity", true);
}
return true;
}
- private void finishActivityResultsLocked(ActivityRecord r, int resultCode, Intent resultData) {
+ private void finishActivityResultsLocked(ActivityRecord r, int resultCode, Intent resultData,
+ NeededUriGrants resultGrants) {
// send the result
ActivityRecord resultTo = r.resultTo;
if (resultTo != null) {
@@ -3932,9 +3934,8 @@
}
}
if (r.info.applicationInfo.uid > 0) {
- mService.mUgmInternal.grantUriPermissionFromIntent(r.info.applicationInfo.uid,
- resultTo.packageName, resultData,
- resultTo.getUriPermissionsLocked(), resultTo.mUserId);
+ mService.mUgmInternal.grantUriPermissionUncheckedFromIntent(resultGrants,
+ resultTo.getUriPermissionsLocked());
}
resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode, resultData);
r.resultTo = null;
@@ -3950,12 +3951,10 @@
r.icicle = null;
}
- /**
- * See {@link #finishActivityLocked(ActivityRecord, int, Intent, String, boolean, boolean)}
- */
final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
- String reason, boolean oomAdj) {
- return finishActivityLocked(r, resultCode, resultData, reason, oomAdj, !PAUSE_IMMEDIATELY);
+ NeededUriGrants resultGrants, String reason, boolean oomAdj) {
+ return finishActivityLocked(r, resultCode, resultData, resultGrants, reason, oomAdj,
+ !PAUSE_IMMEDIATELY);
}
/**
@@ -3963,7 +3962,7 @@
* list, or false if it is still in the list and will be removed later.
*/
final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
- String reason, boolean oomAdj, boolean pauseImmediately) {
+ NeededUriGrants resultGrants, String reason, boolean oomAdj, boolean pauseImmediately) {
if (r.finishing) {
Slog.w(TAG, "Duplicate finish request for " + r);
return false;
@@ -3993,7 +3992,7 @@
adjustFocusedActivityStack(r, "finishActivity");
- finishActivityResultsLocked(r, resultCode, resultData);
+ finishActivityResultsLocked(r, resultCode, resultData, resultGrants);
final boolean endTask = index <= 0 && !task.isClearingToReuseTask();
final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
@@ -4226,8 +4225,9 @@
return false;
}
- final boolean navigateUpToLocked(ActivityRecord srec, Intent destIntent, int resultCode,
- Intent resultData) {
+ final boolean navigateUpToLocked(ActivityRecord srec, Intent destIntent,
+ NeededUriGrants destGrants, int resultCode, Intent resultData,
+ NeededUriGrants resultGrants) {
final TaskRecord task = srec.getTaskRecord();
final ArrayList<ActivityRecord> activities = task.mActivities;
final int start = activities.indexOf(srec);
@@ -4274,7 +4274,8 @@
final long origId = Binder.clearCallingIdentity();
for (int i = start; i > finishTo; i--) {
ActivityRecord r = activities.get(i);
- requestFinishActivityLocked(r.appToken, resultCode, resultData, "navigate-up", true);
+ requestFinishActivityLocked(r.appToken, resultCode, resultData, resultGrants,
+ "navigate-up", true);
// Only return the supplied result for the first activity finished
resultCode = Activity.RESULT_CANCELED;
resultData = null;
@@ -4288,7 +4289,7 @@
parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
(destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
parent.deliverNewIntentLocked(srec.info.applicationInfo.uid, destIntent,
- srec.packageName);
+ destGrants, srec.packageName);
} else {
try {
ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
@@ -4312,7 +4313,7 @@
foundParentInTask = false;
}
requestFinishActivityLocked(parent.appToken, resultCode,
- resultData, "navigate-top", true);
+ resultData, resultGrants, "navigate-top", true);
}
}
Binder.restoreCallingIdentity(origId);
@@ -4397,7 +4398,7 @@
}
private void removeActivityFromHistoryLocked(ActivityRecord r, String reason) {
- finishActivityResultsLocked(r, Activity.RESULT_CANCELED, null);
+ finishActivityResultsLocked(r, Activity.RESULT_CANCELED, null, null);
r.makeFinishingLocked();
if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE,
"Removing activity " + r + " from stack callers=" + Debug.getCallers(5));
@@ -5129,7 +5130,8 @@
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
- finishActivityLocked(r, Activity.RESULT_CANCELED, null, "close-sys", true);
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, null,
+ "close-sys", true);
}
}
}
@@ -5173,8 +5175,8 @@
didSomething = true;
Slog.i(TAG, " Force finishing activity " + r);
lastTask = r.getTaskRecord();
- finishActivityLocked(r, Activity.RESULT_CANCELED, null, "force-stop",
- true);
+ finishActivityLocked(r, Activity.RESULT_CANCELED, null, null,
+ "force-stop", true);
}
}
}
@@ -5228,8 +5230,8 @@
final ArrayList<ActivityRecord> activities = mTaskHistory.get(top).mActivities;
int activityTop = activities.size() - 1;
if (activityTop >= 0) {
- finishActivityLocked(activities.get(activityTop), Activity.RESULT_CANCELED, null,
- "unhandled-back", true);
+ finishActivityLocked(activities.get(activityTop), Activity.RESULT_CANCELED,
+ null, null, "unhandled-back", true);
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index c992a69..8d2a92a 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -140,6 +140,7 @@
import com.android.server.am.ActivityManagerService;
import com.android.server.am.EventLogTags;
import com.android.server.am.UserState;
+import com.android.server.uri.NeededUriGrants;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -403,14 +404,17 @@
final int startFlags;
final ActivityStack stack;
final WindowProcessController callerApp;
+ final NeededUriGrants neededGrants;
- PendingActivityLaunch(ActivityRecord _r, ActivityRecord _sourceRecord,
- int _startFlags, ActivityStack _stack, WindowProcessController app) {
- r = _r;
- sourceRecord = _sourceRecord;
- startFlags = _startFlags;
- stack = _stack;
- callerApp = app;
+ PendingActivityLaunch(ActivityRecord r, ActivityRecord sourceRecord,
+ int startFlags, ActivityStack stack, WindowProcessController callerApp,
+ NeededUriGrants neededGrants) {
+ this.r = r;
+ this.sourceRecord = sourceRecord;
+ this.startFlags = startFlags;
+ this.stack = stack;
+ this.callerApp = callerApp;
+ this.neededGrants = neededGrants;
}
void sendErrorResult(String message) {
@@ -874,8 +878,8 @@
Slog.e(TAG, "Second failure launching "
+ r.intent.getComponent().flattenToShortString() + ", giving up", e);
proc.appDied();
- stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
- "2nd-crash", false);
+ stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED,
+ null, null, "2nd-crash", false);
return false;
}
@@ -1020,7 +1024,7 @@
if (resultRecord != null) {
resultStack.sendActivityResultLocked(-1,
resultRecord, resultWho, requestCode,
- Activity.RESULT_CANCELED, null);
+ Activity.RESULT_CANCELED, null, null);
}
final String msg;
if (actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 919141c..738d143 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -462,7 +462,7 @@
"pendingActivityLaunch");
try {
starter.startResolvedActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags,
- resume, pal.r.pendingOptions, null);
+ resume, pal.r.pendingOptions, null, pal.neededGrants);
} catch (Exception e) {
Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
pal.sendErrorResult(e.getMessage());
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 1718ac6..fb18569 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -127,6 +127,7 @@
import com.android.server.am.EventLogTags;
import com.android.server.am.PendingIntentRecord;
import com.android.server.pm.InstantAppResolver;
+import com.android.server.uri.NeededUriGrants;
import com.android.server.wm.ActivityStackSupervisor.PendingActivityLaunch;
import com.android.server.wm.LaunchParamsController.LaunchParams;
@@ -549,7 +550,8 @@
*/
int startResolvedActivity(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
- int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask) {
+ int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
+ NeededUriGrants neededGrants) {
try {
mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(r.intent);
mLastStartReason = "startResolvedActivity";
@@ -557,7 +559,7 @@
mLastStartActivityRecord[0] = r;
mLastStartActivityResult = startActivity(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, mLastStartActivityRecord,
- false /* restrictedBgActivity */);
+ false /* restrictedBgActivity */, neededGrants);
mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(mLastStartActivityResult,
mLastStartActivityRecord[0]);
return mLastStartActivityResult;
@@ -576,6 +578,33 @@
boolean allowPendingRemoteAnimationRegistryLookup,
PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
+ // Carefully collect grants without holding lock
+ NeededUriGrants neededGrants = null;
+ if (aInfo != null) {
+ neededGrants = mService.mUgmInternal.checkGrantUriPermissionFromIntent(
+ resolveCallingUid(mRequest.caller), intent, aInfo.applicationInfo.packageName,
+ UserHandle.getUserId(aInfo.applicationInfo.uid));
+ }
+
+ return startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo,
+ voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,
+ callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
+ ignoreTargetSecurity, componentSpecified, outActivity, inTask, reason,
+ allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent,
+ allowBackgroundActivityStart, neededGrants);
+ }
+
+ private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
+ String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
+ IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+ IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
+ String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
+ SafeActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
+ ActivityRecord[] outActivity, TaskRecord inTask, String reason,
+ boolean allowPendingRemoteAnimationRegistryLookup,
+ PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart,
+ NeededUriGrants neededGrants) {
+
if (TextUtils.isEmpty(reason)) {
throw new IllegalArgumentException("Need to specify a reason.");
}
@@ -588,7 +617,7 @@
callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
options, ignoreTargetSecurity, componentSpecified, mLastStartActivityRecord,
inTask, allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent,
- allowBackgroundActivityStart);
+ allowBackgroundActivityStart, neededGrants);
if (outActivity != null) {
// mLastStartActivityRecord[0] is set in the call to startActivity above.
@@ -619,7 +648,8 @@
SafeActivityOptions options,
boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup,
- PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
+ PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart,
+ NeededUriGrants neededGrants) {
mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
int err = ActivityManager.START_SUCCESS;
// Pull the optional Ephemeral Installer-only bundle out of the options early.
@@ -754,7 +784,7 @@
if (err != START_SUCCESS) {
if (resultRecord != null) {
resultStack.sendActivityResultLocked(
- -1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null);
+ -1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null, null);
}
SafeActivityOptions.abort(options);
return err;
@@ -814,12 +844,16 @@
callingPid = mInterceptor.mCallingPid;
callingUid = mInterceptor.mCallingUid;
checkedOptions = mInterceptor.mActivityOptions;
+
+ // The interception target shouldn't get any permission grants
+ // intended for the original destination
+ neededGrants = null;
}
if (abort) {
if (resultRecord != null) {
resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
- RESULT_CANCELED, null);
+ RESULT_CANCELED, null, null);
}
// We pretend to the caller that it was really started, but
// they will just get a cancel result.
@@ -875,6 +909,10 @@
aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,
null /*profilerInfo*/);
+ // The permissions review target shouldn't get any permission
+ // grants intended for the original destination
+ neededGrants = null;
+
if (DEBUG_PERMISSIONS_REVIEW) {
final ActivityStack focusedStack =
mRootActivityContainer.getTopDisplayFocusedStack();
@@ -897,6 +935,10 @@
callingPid = realCallingPid;
aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/);
+
+ // The ephemeral installer shouldn't get any permission grants
+ // intended for the original destination
+ neededGrants = null;
}
ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
@@ -923,7 +965,7 @@
realCallingPid, realCallingUid, "Activity start")) {
if (!(restrictedBgActivity && handleBackgroundActivityAbort(r))) {
mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
- sourceRecord, startFlags, stack, callerApp));
+ sourceRecord, startFlags, stack, callerApp, neededGrants));
}
ActivityOptions.abort(checkedOptions);
return ActivityManager.START_SWITCHES_CANCELED;
@@ -934,7 +976,8 @@
mController.doPendingActivityLaunches(false);
final int res = startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
- true /* doResume */, checkedOptions, inTask, outActivity, restrictedBgActivity);
+ true /* doResume */, checkedOptions, inTask, outActivity, restrictedBgActivity,
+ neededGrants);
mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outActivity[0]);
return res;
}
@@ -1232,9 +1275,15 @@
}
}
}
+
// Collect information about the target of the Intent.
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
+ // Carefully collect grants without holding lock
+ NeededUriGrants neededGrants = mService.mUgmInternal.checkGrantUriPermissionFromIntent(
+ resolveCallingUid(mRequest.caller), intent, aInfo.applicationInfo.packageName,
+ UserHandle.getUserId(aInfo.applicationInfo.uid));
+
synchronized (mService.mGlobalLock) {
final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack();
stack.mConfigWillChange = globalConfig != null
@@ -1311,7 +1360,7 @@
callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason,
allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent,
- allowBackgroundActivityStart);
+ allowBackgroundActivityStart, neededGrants);
Binder.restoreCallingIdentity(origId);
@@ -1406,14 +1455,16 @@
private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
- int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
- ActivityRecord[] outActivity, boolean restrictedBgActivity) {
+ int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
+ ActivityRecord[] outActivity, boolean restrictedBgActivity,
+ NeededUriGrants neededGrants) {
int result = START_CANCELED;
final ActivityStack startedActivityStack;
try {
mService.mWindowManager.deferSurfaceLayout();
result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
- startFlags, doResume, options, inTask, outActivity, restrictedBgActivity);
+ startFlags, doResume, options, inTask, outActivity, restrictedBgActivity,
+ neededGrants);
} finally {
final ActivityStack currentStack = r.getActivityStack();
startedActivityStack = currentStack != null ? currentStack : mTargetStack;
@@ -1438,7 +1489,8 @@
final ActivityStack stack = mStartActivity.getActivityStack();
if (stack != null) {
stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
- null /* intentResultData */, "startActivity", true /* oomAdj */);
+ null /* resultData */, null /* resultGrants */,
+ "startActivity", true /* oomAdj */);
}
}
mService.mWindowManager.continueSurfaceLayout();
@@ -1467,7 +1519,7 @@
if (resultRecord != null) {
ActivityStack resultStack = resultRecord.getActivityStack();
resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
- RESULT_CANCELED, null);
+ RESULT_CANCELED, null, null);
}
// We pretend to the caller that it was really started to make it backward compatible, but
// they will just get a cancel result.
@@ -1479,7 +1531,8 @@
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
- ActivityRecord[] outActivity, boolean restrictedBgActivity) {
+ ActivityRecord[] outActivity, boolean restrictedBgActivity,
+ NeededUriGrants neededGrants) {
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor, restrictedBgActivity);
@@ -1629,7 +1682,7 @@
if (sourceStack != null) {
sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
- null /* data */);
+ null /* resultData */, null /* resultGrants */);
}
ActivityOptions.abort(mOptions);
return START_CLASS_NOT_FOUND;
@@ -1696,8 +1749,8 @@
return result;
}
- mService.mUgmInternal.grantUriPermissionFromIntent(mCallingUid, mStartActivity.packageName,
- mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.mUserId);
+ mService.mUgmInternal.grantUriPermissionUncheckedFromIntent(neededGrants,
+ mStartActivity.getUriPermissionsLocked());
mService.getPackageManagerInternalLocked().grantEphemeralAccess(
mStartActivity.mUserId, mIntent, UserHandle.getAppId(mStartActivity.appInfo.uid),
UserHandle.getAppId(mCallingUid));
@@ -1933,7 +1986,7 @@
Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
sourceStack.sendActivityResultLocked(-1 /* callingUid */, mStartActivity.resultTo,
mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED,
- null /* data */);
+ null /* resultData */, null /* resultGrants */);
mStartActivity.resultTo = null;
}
}
@@ -2362,8 +2415,13 @@
}
ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTaskRecord());
- activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
+
+ Intent intent = mStartActivity.intent;
+ NeededUriGrants intentGrants = mService.mUgmInternal.checkGrantUriPermissionFromIntent(
+ mCallingUid, intent, activity.packageName, activity.mUserId);
+ activity.deliverNewIntentLocked(mCallingUid, intent, intentGrants,
mStartActivity.launchedFromPackage);
+
mIntentDelivered = true;
}
@@ -2742,6 +2800,18 @@
}
}
+ private int resolveCallingUid(IApplicationThread caller) {
+ if (caller != null) {
+ synchronized (mService.mGlobalLock) {
+ final WindowProcessController callerApp = mService.getProcessController(caller);
+ if (callerApp != null) {
+ return callerApp.mInfo.uid;
+ }
+ }
+ }
+ return -1;
+ }
+
private boolean isLaunchModeOneOf(int mode1, int mode2) {
return mode1 == mLaunchMode || mode2 == mLaunchMode;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 2e5b9fd..b123aa5 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -265,6 +265,7 @@
import com.android.server.firewall.IntentFirewall;
import com.android.server.pm.UserManagerService;
import com.android.server.policy.PermissionPolicyInternal;
+import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.vr.VrManagerInternal;
@@ -1540,11 +1541,19 @@
throw new IllegalArgumentException("File descriptors passed in Intent");
}
+ final ActivityRecord r;
synchronized (mGlobalLock) {
- ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ r = ActivityRecord.isInStackLocked(token);
if (r == null) {
return true;
}
+ }
+
+ // Carefully collect grants without holding lock
+ final NeededUriGrants resultGrants = mUgmInternal.checkGrantUriPermissionFromIntent(
+ Binder.getCallingUid(), resultData, r.packageName, r.mUserId);
+
+ synchronized (mGlobalLock) {
// Keep track of the root activity of the task before we finish it
final TaskRecord tr = r.getTaskRecord();
ActivityRecord rootR = tr.getRootActivity();
@@ -1606,7 +1615,7 @@
r.mRelaunchReason = RELAUNCH_REASON_NONE;
} else {
res = tr.getStack().requestFinishActivityLocked(token, resultCode,
- resultData, "app-request", true);
+ resultData, resultGrants, "app-request", true);
if (!res) {
Slog.i(TAG, "Failed to finish by app-request");
}
@@ -2132,14 +2141,23 @@
@Override
public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode,
Intent resultData) {
+ final ActivityRecord r;
+ synchronized (mGlobalLock) {
+ r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return false;
+ }
+ }
+
+ // Carefully collect grants without holding lock
+ final NeededUriGrants destGrants = mUgmInternal.checkGrantUriPermissionFromIntent(
+ Binder.getCallingUid(), destIntent, r.packageName, r.mUserId);
+ final NeededUriGrants resultGrants = mUgmInternal.checkGrantUriPermissionFromIntent(
+ Binder.getCallingUid(), resultData, r.packageName, r.mUserId);
synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r != null) {
- return r.getActivityStack().navigateUpToLocked(
- r, destIntent, resultCode, resultData);
- }
- return false;
+ return r.getActivityStack().navigateUpToLocked(
+ r, destIntent, destGrants, resultCode, resultData, resultGrants);
}
}
@@ -5867,8 +5885,10 @@
*/
Intent getSecondaryHomeIntent(String preferredPackage) {
final Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
- if (preferredPackage == null) {
- // Using the component stored in config if no package name.
+ final boolean useSystemProvidedLauncher = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
+ if (preferredPackage == null || useSystemProvidedLauncher) {
+ // Using the component stored in config if no package name or forced.
final String secondaryHomeComponent = mContext.getResources().getString(
com.android.internal.R.string.config_secondaryHomeComponent);
intent.setComponent(ComponentName.unflattenFromString(secondaryHomeComponent));
@@ -6595,14 +6615,23 @@
@Override
public void sendActivityResult(int callingUid, IBinder activityToken, String resultWho,
- int requestCode, int resultCode, Intent data) {
+ int requestCode, int resultCode, Intent resultData) {
+ final ActivityRecord r;
synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
- if (r != null && r.getActivityStack() != null) {
- r.getActivityStack().sendActivityResultLocked(callingUid, r, resultWho,
- requestCode, resultCode, data);
+ r = ActivityRecord.isInStackLocked(activityToken);
+ if (r == null || r.getActivityStack() == null) {
+ return;
}
}
+
+ // Carefully collect grants without holding lock
+ final NeededUriGrants resultGrants = mUgmInternal.checkGrantUriPermissionFromIntent(
+ Binder.getCallingUid(), resultData, r.packageName, r.mUserId);
+
+ synchronized (mGlobalLock) {
+ r.getActivityStack().sendActivityResultLocked(callingUid, r, resultWho,
+ requestCode, resultCode, resultData, resultGrants);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 298b302..4a0bf02a 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -1442,7 +1442,7 @@
mActivities.remove(activityNdx);
--activityNdx;
--numActivities;
- } else if (mStack.finishActivityLocked(r, Activity.RESULT_CANCELED, null,
+ } else if (mStack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, null,
reason, false, pauseImmediately)) {
--activityNdx;
--numActivities;
@@ -1497,8 +1497,8 @@
if (opts != null) {
ret.updateOptionsLocked(opts);
}
- if (mStack != null && mStack.finishActivityLocked(
- r, Activity.RESULT_CANCELED, null, "clear-task-stack", false)) {
+ if (mStack != null && mStack.finishActivityLocked(r, Activity.RESULT_CANCELED,
+ null, null, "clear-task-stack", false)) {
--activityNdx;
--numActivities;
}
@@ -1512,8 +1512,8 @@
&& !ActivityStarter.isDocumentLaunchesIntoExisting(launchFlags)) {
if (!ret.finishing) {
if (mStack != null) {
- mStack.finishActivityLocked(
- ret, Activity.RESULT_CANCELED, null, "clear-task-top", false);
+ mStack.finishActivityLocked(ret, Activity.RESULT_CANCELED,
+ null, null, "clear-task-top", false);
}
return null;
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 2ad25cf..fbd4ec7 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -623,7 +623,7 @@
final ActivityRecord r = activities.get(i);
if (!r.finishing && r.isInStackLocked()) {
r.getActivityStack().finishActivityLocked(r, Activity.RESULT_CANCELED,
- null, "finish-heavy", true);
+ null, null, "finish-heavy", true);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 89fd33b..4b96935 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1457,10 +1457,12 @@
void clearPolicyVisibilityFlag(int policyVisibilityFlag) {
mPolicyVisibility &= ~policyVisibilityFlag;
+ mWmService.scheduleAnimationLocked();
}
void setPolicyVisibilityFlag(int policyVisibilityFlag) {
mPolicyVisibility |= policyVisibilityFlag;
+ mWmService.scheduleAnimationLocked();
}
private boolean isLegacyPolicyVisibility() {
@@ -3934,7 +3936,7 @@
boolean performShowLocked() {
if (isHiddenFromUserLocked()) {
if (DEBUG_VISIBILITY) Slog.w(TAG, "hiding " + this + ", belonging to " + mOwnerUid);
- hideLw(false);
+ clearPolicyVisibilityFlag(VISIBLE_FOR_USER);
return false;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4e1cac9..203704b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -749,10 +749,12 @@
if (!disableOtaDexopt) {
traceBeginAndSlog("StartOtaDexOptService");
try {
+ Watchdog.getInstance().pauseWatchingCurrentThread("moveab");
OtaDexoptService.main(mSystemContext, mPackageManagerService);
} catch (Throwable e) {
reportWtf("starting OtaDexOptService", e);
} finally {
+ Watchdog.getInstance().resumeWatchingCurrentThread("moveab");
traceEnd();
}
}
diff --git a/services/net/java/android/net/NetworkMonitorManager.java b/services/net/java/android/net/NetworkMonitorManager.java
new file mode 100644
index 0000000..0f41302
--- /dev/null
+++ b/services/net/java/android/net/NetworkMonitorManager.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * A convenience wrapper for INetworkMonitor.
+ *
+ * Wraps INetworkMonitor calls, making them a bit more friendly to use. Currently handles:
+ * - Clearing calling identity
+ * - Ignoring RemoteExceptions
+ * - Converting to stable parcelables
+ *
+ * By design, all methods on INetworkMonitor are asynchronous oneway IPCs and are thus void. All the
+ * wrapper methods in this class return a boolean that callers can use to determine whether
+ * RemoteException was thrown.
+ */
+public class NetworkMonitorManager {
+
+ @NonNull private final INetworkMonitor mNetworkMonitor;
+ @NonNull private final String mTag;
+
+ public NetworkMonitorManager(@NonNull INetworkMonitor networkMonitorManager,
+ @NonNull String tag) {
+ mNetworkMonitor = networkMonitorManager;
+ mTag = tag;
+ }
+
+ public NetworkMonitorManager(@NonNull INetworkMonitor networkMonitorManager) {
+ this(networkMonitorManager, NetworkMonitorManager.class.getSimpleName());
+ }
+
+ private void log(String s, Throwable e) {
+ Log.e(mTag, s, e);
+ }
+
+ // CHECKSTYLE:OFF Generated code
+
+ public boolean start() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mNetworkMonitor.start();
+ return true;
+ } catch (RemoteException e) {
+ log("Error in start", e);
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public boolean launchCaptivePortalApp() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mNetworkMonitor.launchCaptivePortalApp();
+ return true;
+ } catch (RemoteException e) {
+ log("Error in launchCaptivePortalApp", e);
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public boolean notifyCaptivePortalAppFinished(int response) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mNetworkMonitor.notifyCaptivePortalAppFinished(response);
+ return true;
+ } catch (RemoteException e) {
+ log("Error in notifyCaptivePortalAppFinished", e);
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public boolean setAcceptPartialConnectivity() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mNetworkMonitor.setAcceptPartialConnectivity();
+ return true;
+ } catch (RemoteException e) {
+ log("Error in setAcceptPartialConnectivity", e);
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public boolean forceReevaluation(int uid) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mNetworkMonitor.forceReevaluation(uid);
+ return true;
+ } catch (RemoteException e) {
+ log("Error in forceReevaluation", e);
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public boolean notifyPrivateDnsChanged(PrivateDnsConfigParcel config) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mNetworkMonitor.notifyPrivateDnsChanged(config);
+ return true;
+ } catch (RemoteException e) {
+ log("Error in notifyPrivateDnsChanged", e);
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public boolean notifyDnsResponse(int returnCode) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mNetworkMonitor.notifyDnsResponse(returnCode);
+ return true;
+ } catch (RemoteException e) {
+ log("Error in notifyDnsResponse", e);
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public boolean notifyNetworkConnected(LinkProperties lp, NetworkCapabilities nc) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mNetworkMonitor.notifyNetworkConnected(lp, nc);
+ return true;
+ } catch (RemoteException e) {
+ log("Error in notifyNetworkConnected", e);
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public boolean notifyNetworkDisconnected() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mNetworkMonitor.notifyNetworkDisconnected();
+ return true;
+ } catch (RemoteException e) {
+ log("Error in notifyNetworkDisconnected", e);
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public boolean notifyLinkPropertiesChanged(LinkProperties lp) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mNetworkMonitor.notifyLinkPropertiesChanged(lp);
+ return true;
+ } catch (RemoteException e) {
+ log("Error in notifyLinkPropertiesChanged", e);
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public boolean notifyNetworkCapabilitiesChanged(NetworkCapabilities nc) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mNetworkMonitor.notifyNetworkCapabilitiesChanged(nc);
+ return true;
+ } catch (RemoteException e) {
+ log("Error in notifyNetworkCapabilitiesChanged", e);
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ // CHECKSTYLE:ON Generated code
+}
diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java
index 09c9b6d..99da637 100644
--- a/services/net/java/android/net/NetworkStackClient.java
+++ b/services/net/java/android/net/NetworkStackClient.java
@@ -33,6 +33,7 @@
import android.net.ip.IIpClientCallbacks;
import android.net.util.SharedLog;
import android.os.Binder;
+import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
import android.os.Process;
@@ -41,6 +42,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.Slog;
@@ -60,14 +62,22 @@
private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
private static final String IN_PROCESS_SUFFIX = ".InProcess";
private static final String PREFS_FILE = "NetworkStackClientPrefs.xml";
- private static final String PREF_KEY_LAST_CRASH_UPTIME = "lastcrash";
+ private static final String PREF_KEY_LAST_CRASH_TIME = "lastcrash_time";
private static final String CONFIG_MIN_CRASH_INTERVAL_MS = "min_crash_interval";
+ private static final String CONFIG_MIN_UPTIME_BEFORE_CRASH_MS = "min_uptime_before_crash";
+ private static final String CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH =
+ "always_ratelimit_networkstack_crash";
// Even if the network stack is lost, do not crash the system more often than this.
// Connectivity would be broken, but if the user needs the device for something urgent
// (like calling emergency services) we should not bootloop the device.
// This is the default value: the actual value can be adjusted via device config.
- private static final long DEFAULT_MIN_CRASH_INTERVAL_MS = 6 * 3_600_000L;
+ private static final long DEFAULT_MIN_CRASH_INTERVAL_MS = 6 * DateUtils.HOUR_IN_MILLIS;
+
+ // Even if the network stack is lost, do not crash the system server if it was less than
+ // this much after boot. This avoids bootlooping the device, and crashes should address very
+ // infrequent failures, not failures on boot.
+ private static final long DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
private static NetworkStackClient sInstance;
@@ -83,12 +93,6 @@
private volatile boolean mWasSystemServerInitialized = false;
- /**
- * If non-zero, indicates that the last framework start happened after a crash of the
- * NetworkStack which was at the specified uptime.
- */
- private volatile long mLastCrashUptime = 0L;
-
@GuardedBy("mHealthListeners")
private final ArraySet<NetworkStackHealthListener> mHealthListeners = new ArraySet<>();
@@ -258,12 +262,6 @@
public void start(Context context) {
log("Starting network stack");
- final SharedPreferences prefs = getSharedPreferences(context);
- mLastCrashUptime = prefs.getLong(PREF_KEY_LAST_CRASH_UPTIME, 0L);
- // Remove the preference after getting the last crash uptime, so mLastCrashUptime always
- // indicates this is the first start since the last crash.
- prefs.edit().remove(PREF_KEY_LAST_CRASH_UPTIME).commit();
-
final PackageManager pm = context.getPackageManager();
// Try to bind in-process if the device was shipped with an in-process version
@@ -344,21 +342,40 @@
logWtf(message, null);
// uptime is monotonic even after a framework restart
final long uptime = SystemClock.elapsedRealtime();
+ final long now = System.currentTimeMillis();
final long minCrashIntervalMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY,
CONFIG_MIN_CRASH_INTERVAL_MS, DEFAULT_MIN_CRASH_INTERVAL_MS);
+ final long minUptimeBeforeCrash = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY,
+ CONFIG_MIN_UPTIME_BEFORE_CRASH_MS, DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS);
+ final boolean alwaysRatelimit = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CONNECTIVITY,
+ CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH, false);
- // Either the framework was not restarted after a crash of the NetworkStack, or the min
- // crash interval has passed since then.
- if (mLastCrashUptime == 0L || uptime - mLastCrashUptime > minCrashIntervalMs) {
+ final SharedPreferences prefs = getSharedPreferences(context);
+ final long lastCrashTime = tryGetLastCrashTime(prefs);
+
+ // Only crash if there was enough time since boot, and (if known) enough time passed since
+ // the last crash.
+ // time and lastCrashTime may be unreliable if devices have incorrect clock time, but they
+ // are only used to limit the number of crashes compared to only using the time since boot,
+ // which would also be OK behavior by itself.
+ // - If lastCrashTime is incorrectly more than the current time, only look at uptime
+ // - If it is much less than current time, only look at uptime
+ // - If current time is during the next few hours after last crash time, don't crash.
+ // Considering that this only matters if last boot was some time ago, it's likely that
+ // time will be set correctly. Otherwise, not crashing is not a big problem anyway. Being
+ // in this last state would also not last for long since the window is only a few hours.
+ final boolean alwaysCrash = Build.IS_DEBUGGABLE && !alwaysRatelimit;
+ final boolean justBooted = uptime < minUptimeBeforeCrash;
+ final boolean haveLastCrashTime = (lastCrashTime != 0) && (lastCrashTime < now);
+ final boolean haveKnownRecentCrash =
+ haveLastCrashTime && (now < lastCrashTime + minCrashIntervalMs);
+ if (alwaysCrash || (!justBooted && !haveKnownRecentCrash)) {
// The system is not bound to its network stack (for example due to a crash in the
// network stack process): better crash rather than stay in a bad state where all
// networking is broken.
// Using device-encrypted SharedPreferences as DeviceConfig does not have a synchronous
// API to persist settings before a crash.
- final SharedPreferences prefs = getSharedPreferences(context);
- if (!prefs.edit().putLong(PREF_KEY_LAST_CRASH_UPTIME, uptime).commit()) {
- logWtf("Could not persist last crash uptime", null);
- }
+ tryWriteLastCrashTime(prefs, now);
throw new IllegalStateException(message);
}
@@ -375,11 +392,36 @@
}
}
- private SharedPreferences getSharedPreferences(Context context) {
- final File prefsFile = new File(
- Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM), PREFS_FILE);
- return context.createDeviceProtectedStorageContext()
- .getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
+ @Nullable
+ private SharedPreferences getSharedPreferences(@NonNull Context context) {
+ try {
+ final File prefsFile = new File(
+ Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM), PREFS_FILE);
+ return context.createDeviceProtectedStorageContext()
+ .getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
+ } catch (Throwable e) {
+ logWtf("Error loading shared preferences", e);
+ return null;
+ }
+ }
+
+ private long tryGetLastCrashTime(@Nullable SharedPreferences prefs) {
+ if (prefs == null) return 0L;
+ try {
+ return prefs.getLong(PREF_KEY_LAST_CRASH_TIME, 0L);
+ } catch (Throwable e) {
+ logWtf("Error getting last crash time", e);
+ return 0L;
+ }
+ }
+
+ private void tryWriteLastCrashTime(@Nullable SharedPreferences prefs, long value) {
+ if (prefs == null) return;
+ try {
+ prefs.edit().putLong(PREF_KEY_LAST_CRASH_TIME, value).commit();
+ } catch (Throwable e) {
+ logWtf("Error writing last crash time", e);
+ }
}
/**
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 18c524a..748c23a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -16,6 +16,7 @@
package com.android.server.job;
+import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -162,6 +163,56 @@
/**
* Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
+ * with the correct delay and deadline constraints if the periodic job is scheduled with the
+ * minimum possible period.
+ */
+ @Test
+ public void testGetRescheduleJobForPeriodic_minPeriod() {
+ final long now = sElapsedRealtimeClock.millis();
+ JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow",
+ createJobInfo().setPeriodic(15 * MINUTE_IN_MILLIS));
+ final long nextWindowStartTime = now + 15 * MINUTE_IN_MILLIS;
+ final long nextWindowEndTime = now + 30 * MINUTE_IN_MILLIS;
+
+ for (int i = 0; i < 25; i++) {
+ JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+ advanceElapsedClock(30_000); // 30 seconds
+ }
+
+ for (int i = 0; i < 5; i++) {
+ // Window buffering in last 1/6 of window.
+ JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ assertEquals(nextWindowStartTime + i * 30_000, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+ advanceElapsedClock(30_000); // 30 seconds
+ }
+ }
+
+ /**
+ * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
+ * with the correct delay and deadline constraints if the periodic job is scheduled with a
+ * period that's too large.
+ */
+ @Test
+ public void testGetRescheduleJobForPeriodic_largePeriod() {
+ final long now = sElapsedRealtimeClock.millis();
+ JobStatus job = createJobStatus("testGetRescheduleJobForPeriodic_insideWindow",
+ createJobInfo().setPeriodic(2 * 365 * DAY_IN_MILLIS));
+ assertEquals(now, job.getEarliestRunTime());
+ // Periods are capped at 365 days (1 year).
+ assertEquals(now + 365 * DAY_IN_MILLIS, job.getLatestRunTimeElapsed());
+ final long nextWindowStartTime = now + 365 * DAY_IN_MILLIS;
+ final long nextWindowEndTime = nextWindowStartTime + 365 * DAY_IN_MILLIS;
+
+ JobStatus rescheduledJob = mService.getRescheduleJobForPeriodic(job);
+ assertEquals(nextWindowStartTime, rescheduledJob.getEarliestRunTime());
+ assertEquals(nextWindowEndTime, rescheduledJob.getLatestRunTimeElapsed());
+ }
+
+ /**
+ * Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
* with the correct delay and deadline constraints if the periodic job is completed and
* rescheduled while run in its expected running window.
*/
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 1db6b8e..f07a5b5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -2450,6 +2450,8 @@
timeout(remainingTimeMs + 2 * SECOND_IN_MILLIS).times(1))
.onControllerStateChanged();
assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+ assertEquals(JobSchedulerService.sElapsedRealtimeClock.millis(),
+ jobStatus.getWhenStandbyDeferred());
}
/**
@@ -2606,6 +2608,8 @@
"testStartAlarmScheduled_TimingSessionCount_AllowedTime", 42);
mQuotaController.maybeStartTrackingJobLocked(throttledJob, null);
assertFalse(throttledJob.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA));
+ assertEquals(JobSchedulerService.sElapsedRealtimeClock.millis(),
+ throttledJob.getWhenStandbyDeferred());
ExecutionStats stats = mQuotaController.getExecutionStatsLocked(SOURCE_USER_ID,
SOURCE_PACKAGE, standbyBucket);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 757267e5..4c0b582 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -940,7 +940,8 @@
homeStask.removeTask(homeTask, "testAdjustFocusedStack", REMOVE_TASK_MODE_DESTROYING);
// Finish the only activity.
- mStack.finishActivityLocked(topActivity, 0 /* resultCode */, null /* resultData */,
+ mStack.finishActivityLocked(topActivity, 0 /* resultCode */,
+ null /* resultData */, null /* resultGrants */,
"testAdjustFocusedStack", false /* oomAdj */);
// Although home stack is empty, it should still be the focused stack.
assertEquals(homeStask, mDefaultDisplay.getFocusedStack());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
index a7bbe6e..2d12006 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java
@@ -82,12 +82,12 @@
wpc.setThread(mock(IApplicationThread.class));
mController.addPendingActivityLaunch(
- new PendingActivityLaunch(activity, source, startFlags, stack, wpc));
+ new PendingActivityLaunch(activity, source, startFlags, stack, wpc, null));
final boolean resume = random.nextBoolean();
mController.doPendingActivityLaunches(resume);
verify(mStarter, times(1)).startResolvedActivity(eq(activity), eq(source), eq(null),
- eq(null), eq(startFlags), eq(resume), eq(null), eq(null));
+ eq(null), eq(startFlags), eq(resume), eq(null), eq(null), eq(null));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 0f7b35c..c77e25f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -30,6 +30,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
@@ -54,6 +55,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
@@ -601,6 +603,73 @@
}
/**
+ * Tests that the default secondary home activity is always picked when it is in forced by
+ * config_useSystemProvidedLauncherForSecondary.
+ */
+ @Test
+ public void testResolveSecondaryHomeActivityForced() throws Exception {
+ Resources resources = mContext.getResources();
+ spyOn(resources);
+ try {
+ // setUp: set secondary launcher and force it.
+ final String defaultSecondaryHome =
+ "com.android.test/com.android.test.TestDefaultSecondaryHome";
+ final ComponentName secondaryComp = ComponentName.unflattenFromString(
+ defaultSecondaryHome);
+ doReturn(defaultSecondaryHome).when(resources).getString(
+ com.android.internal.R.string.config_secondaryHomeComponent);
+ doReturn(true).when(resources).getBoolean(
+ com.android.internal.R.bool.config_useSystemProvidedLauncherForSecondary);
+ final Intent secondaryHomeIntent = mService.getSecondaryHomeIntent(null);
+ assertEquals(secondaryComp, secondaryHomeIntent.getComponent());
+ final ActivityInfo aInfoSecondary = new ActivityInfo();
+ aInfoSecondary.name = secondaryComp.getClassName();
+ aInfoSecondary.applicationInfo = new ApplicationInfo();
+ aInfoSecondary.applicationInfo.packageName = secondaryComp.getPackageName();
+ doReturn(aInfoSecondary).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+ refEq(secondaryHomeIntent));
+ final Intent homeIntent = mService.getHomeIntent();
+ final ActivityInfo aInfoDefault = new ActivityInfo();
+ aInfoDefault.name = "fakeHomeActivity";
+ aInfoDefault.applicationInfo = new ApplicationInfo();
+ aInfoDefault.applicationInfo.packageName = "fakeHomePackage";
+ doReturn(aInfoDefault).when(mRootActivityContainer).resolveHomeActivity(anyInt(),
+ refEq(homeIntent));
+ // Let resolveActivities call to validate both main launcher and second launcher so that
+ // resolveActivities call does not work as enabler for secondary.
+ final List<ResolveInfo> resolutions1 = new ArrayList<>();
+ final ResolveInfo resolveInfo1 = new ResolveInfo();
+ resolveInfo1.activityInfo = new ActivityInfo();
+ resolveInfo1.activityInfo.name = aInfoDefault.name;
+ resolveInfo1.activityInfo.applicationInfo = aInfoDefault.applicationInfo;
+ resolutions1.add(resolveInfo1);
+ doReturn(resolutions1).when(mRootActivityContainer).resolveActivities(anyInt(),
+ refEq(homeIntent));
+ final List<ResolveInfo> resolutions2 = new ArrayList<>();
+ final ResolveInfo resolveInfo2 = new ResolveInfo();
+ resolveInfo2.activityInfo = new ActivityInfo();
+ resolveInfo2.activityInfo.name = aInfoSecondary.name;
+ resolveInfo2.activityInfo.applicationInfo = aInfoSecondary.applicationInfo;
+ resolutions2.add(resolveInfo2);
+ doReturn(resolutions2).when(mRootActivityContainer).resolveActivities(anyInt(),
+ refEq(secondaryHomeIntent));
+ doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
+ any(), anyInt(), anyBoolean());
+
+ // Run the test
+ final Pair<ActivityInfo, Intent> resolvedInfo = mRootActivityContainer
+ .resolveSecondaryHomeActivity(0 /* userId */, 1 /* displayId */);
+ assertEquals(secondaryComp.getClassName(), resolvedInfo.first.name);
+ assertEquals(secondaryComp.getPackageName(),
+ resolvedInfo.first.applicationInfo.packageName);
+ assertEquals(aInfoSecondary.name, resolvedInfo.first.name);
+ } finally {
+ // tearDown
+ reset(resources);
+ }
+ }
+
+ /**
* Tests that secondary home should be selected if default home not support secondary displays
* or there is no matched activity in the same package as selected default home.
*/
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index d77ea6e..e1ffb0f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -77,7 +77,6 @@
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.LocalServices;
-import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.UiThread;
import com.android.server.soundtrigger.SoundTriggerInternal;
@@ -1284,9 +1283,7 @@
mRm.addOnRoleHoldersChangedListenerAsUser(executor, this, UserHandle.ALL);
UserHandle currentUser = UserHandle.of(LocalServices.getService(
ActivityManagerInternal.class).getCurrentUserId());
- SystemServerInitThreadPool.get().submit(() -> onRoleHoldersChanged(
- RoleManager.ROLE_ASSISTANT, currentUser),
- "VoiceInteractionManagerService RoleObserver initialization");
+ onRoleHoldersChanged(RoleManager.ROLE_ASSISTANT, currentUser);
}
private @NonNull String getDefaultRecognizer(@NonNull UserHandle user) {
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 6000b56..cd5fd97 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -95,6 +95,10 @@
private Bundle mExtras;
private Set<String> mPreviousExtraKeys;
private final Object mExtrasLock = new Object();
+ private Uri mAddress;
+ private int mAddressPresentation;
+ private String mCallerDisplayName;
+ private int mCallerDisplayNamePresentation;
private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
@Override
@@ -987,12 +991,67 @@
*/
public final void setAddress(Uri address, int presentation) {
Log.d(this, "setAddress %s", address);
+ mAddress = address;
+ mAddressPresentation = presentation;
for (Listener l : mListeners) {
l.onAddressChanged(this, address, presentation);
}
}
/**
+ * Returns the "address" associated with the conference. This is applicable in two cases:
+ * <ol>
+ * <li>When {@link #setConferenceState(boolean)} is used to mark a conference as
+ * temporarily "not a conference"; we need to present the correct address in the in-call
+ * UI.</li>
+ * <li>When the conference is not hosted on the current device, we need to know the address
+ * information for the purpose of showing the original address to the user, as well as for
+ * logging to the call log.</li>
+ * </ol>
+ * @return The address of the conference, or {@code null} if not applicable.
+ * @hide
+ */
+ public final Uri getAddress() {
+ return mAddress;
+ }
+
+ /**
+ * Returns the address presentation associated with the conference.
+ * <p>
+ * This is applicable in two cases:
+ * <ol>
+ * <li>When {@link #setConferenceState(boolean)} is used to mark a conference as
+ * temporarily "not a conference"; we need to present the correct address in the in-call
+ * UI.</li>
+ * <li>When the conference is not hosted on the current device, we need to know the address
+ * information for the purpose of showing the original address to the user, as well as for
+ * logging to the call log.</li>
+ * </ol>
+ * @return The address of the conference, or {@code null} if not applicable.
+ * @hide
+ */
+ public final int getAddressPresentation() {
+ return mAddressPresentation;
+ }
+
+ /**
+ * @return The caller display name (CNAP).
+ * @hide
+ */
+ public final String getCallerDisplayName() {
+ return mCallerDisplayName;
+ }
+
+ /**
+ * @return The presentation requirements for the handle.
+ * See {@link TelecomManager} for valid values.
+ * @hide
+ */
+ public final int getCallerDisplayNamePresentation() {
+ return mCallerDisplayNamePresentation;
+ }
+
+ /**
* Sets the caller display name (CNAP) of this {@link Conference}. Used when
* {@link #setConferenceState(boolean)} is called to mark a conference temporarily as NOT a
* conference.
@@ -1004,6 +1063,8 @@
*/
public final void setCallerDisplayName(String callerDisplayName, int presentation) {
Log.d(this, "setCallerDisplayName %s", callerDisplayName);
+ mCallerDisplayName = callerDisplayName;
+ mCallerDisplayNamePresentation = presentation;
for (Listener l : mListeners) {
l.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 28e6596..47587c5 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -424,8 +424,16 @@
*/
public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 1 << 10;
+ /**
+ * Set by the framework to indicate that a Conference or Connection is hosted by a device other
+ * than the current one. Used in scenarios where the conference originator is the remote device
+ * and the current device is a participant of that conference.
+ * @hide
+ */
+ public static final int PROPERTY_REMOTELY_HOSTED = 1 << 11;
+
//**********************************************************************************************
- // Next PROPERTY value: 1<<10
+ // Next PROPERTY value: 1<<12
//**********************************************************************************************
/**
@@ -850,6 +858,10 @@
builder.append(isLong ? " PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL" : " ecall");
}
+ if (can(properties, PROPERTY_REMOTELY_HOSTED)) {
+ builder.append(isLong ? " PROPERTY_REMOTELY_HOSTED" : " remote_hst");
+ }
+
builder.append("]");
return builder.toString();
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index c66e92b..626fcc4 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -2056,7 +2056,11 @@
conference.getConnectTimeMillis(),
conference.getConnectionStartElapsedRealTime(),
conference.getStatusHints(),
- conference.getExtras());
+ conference.getExtras(),
+ conference.getAddress(),
+ conference.getAddressPresentation(),
+ conference.getCallerDisplayName(),
+ conference.getCallerDisplayNamePresentation());
mAdapter.addConferenceCall(id, parcelableConference);
mAdapter.setVideoProvider(id, conference.getVideoProvider());
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 3acd83a..04e930c 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -325,6 +325,15 @@
} catch (RemoteException e) {
Log.e(this, e, "Exception trying to query for remote CSs");
}
+ } else {
+ try {
+ // This is not an error condition, so just pass back an empty list.
+ // This happens when querying from a remote connection service, not the connection
+ // manager itself.
+ callback.onResult(Collections.EMPTY_LIST, Collections.EMPTY_LIST);
+ } catch (RemoteException e) {
+ Log.e(this, e, "Exception trying to query for remote CSs");
+ }
}
}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index b281589..60b2172 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -477,7 +477,7 @@
SomeArgs args = SomeArgs.obtain();
args.arg1 = callback;
args.arg2 = callingPackage;
- mHandler.obtainMessage(MSG_QUERY_REMOTE_CALL_SERVICES, callback).sendToTarget();
+ mHandler.obtainMessage(MSG_QUERY_REMOTE_CALL_SERVICES, args).sendToTarget();
}
@Override
diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java
index 636e4b2..ede0594 100644
--- a/telecomm/java/android/telecom/ParcelableConference.java
+++ b/telecomm/java/android/telecom/ParcelableConference.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -42,6 +43,10 @@
private StatusHints mStatusHints;
private Bundle mExtras;
private long mConnectElapsedTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
+ private final Uri mAddress;
+ private final int mAddressPresentation;
+ private final String mCallerDisplayName;
+ private final int mCallerDisplayNamePresentation;
public ParcelableConference(
PhoneAccountHandle phoneAccount,
@@ -54,7 +59,11 @@
long connectTimeMillis,
long connectElapsedTimeMillis,
StatusHints statusHints,
- Bundle extras) {
+ Bundle extras,
+ Uri address,
+ int addressPresentation,
+ String callerDisplayName,
+ int callerDisplayNamePresentation) {
mPhoneAccount = phoneAccount;
mState = state;
mConnectionCapabilities = connectionCapabilities;
@@ -66,6 +75,10 @@
mStatusHints = statusHints;
mExtras = extras;
mConnectElapsedTimeMillis = connectElapsedTimeMillis;
+ mAddress = address;
+ mAddressPresentation = addressPresentation;
+ mCallerDisplayName = callerDisplayName;
+ mCallerDisplayNamePresentation = callerDisplayNamePresentation;
}
@Override
@@ -134,6 +147,14 @@
return mExtras;
}
+ public Uri getHandle() {
+ return mAddress;
+ }
+
+ public int getHandlePresentation() {
+ return mAddressPresentation;
+ }
+
public static final @android.annotation.NonNull Parcelable.Creator<ParcelableConference> CREATOR =
new Parcelable.Creator<ParcelableConference> () {
@Override
@@ -152,10 +173,15 @@
Bundle extras = source.readBundle(classLoader);
int properties = source.readInt();
long connectElapsedTimeMillis = source.readLong();
+ Uri address = source.readParcelable(classLoader);
+ int addressPresentation = source.readInt();
+ String callerDisplayName = source.readString();
+ int callerDisplayNamePresentation = source.readInt();
return new ParcelableConference(phoneAccount, state, capabilities, properties,
connectionIds, videoCallProvider, videoState, connectTimeMillis,
- connectElapsedTimeMillis, statusHints, extras);
+ connectElapsedTimeMillis, statusHints, extras, address, addressPresentation,
+ callerDisplayName, callerDisplayNamePresentation);
}
@Override
@@ -185,5 +211,9 @@
destination.writeBundle(mExtras);
destination.writeInt(mConnectionProperties);
destination.writeLong(mConnectElapsedTimeMillis);
+ destination.writeParcelable(mAddress, 0);
+ destination.writeInt(mAddressPresentation);
+ destination.writeString(mCallerDisplayName);
+ destination.writeInt(mCallerDisplayNamePresentation);
}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a125e31..8ef381d 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1132,6 +1132,7 @@
* <li> 8: Wi-Fi</li>
* <li> 9: WiFi Calling</li>
* <li> 10: VoWifi</li>
+ * <li> 11: %s WiFi Calling</li>
* @hide
*/
public static final String KEY_WFC_SPN_FORMAT_IDX_INT = "wfc_spn_format_idx_int";
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index e010d28..a9521e3 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -302,11 +302,27 @@
* subscription.
*
* Default value is 0.
+ *
+ * @deprecated Replaced by {@link #DATA_ENABLED_OVERRIDE_RULES}
+ * @hide
*/
- /** @hide */
+ @Deprecated
public static final String WHITE_LISTED_APN_DATA = "white_listed_apn_data";
/**
+ * TelephonyProvider column name data_enabled_override_rules.
+ * It's a list of rules for overriding data enabled settings. The syntax is
+ * For example, "mms=nonDefault" indicates enabling data for mms in non-default subscription.
+ * "default=nonDefault&inVoiceCall" indicates enabling data for internet in non-default
+ * subscription and while is in voice call.
+ *
+ * Default value is empty string.
+ *
+ * @hide
+ */
+ public static final String DATA_ENABLED_OVERRIDE_RULES = "data_enabled_override_rules";
+
+ /**
* This constant is to designate a subscription as a Local-SIM Subscription.
* <p> A Local-SIM can be a physical SIM inserted into a sim-slot in the device, or eSIM on the
* device.
@@ -775,14 +791,6 @@
public static final int PROFILE_CLASS_DEFAULT = PROFILE_CLASS_UNSET;
/**
- * TelephonyProvider column name IMSI (International Mobile Subscriber Identity).
- * <P>Type: TEXT </P>
- * @hide
- */
- //TODO: add @SystemApi
- public static final String IMSI = "imsi";
-
- /**
* Broadcast Action: The user has changed one of the default subs related to
* data, phone calls, or sms</p>
*
@@ -2968,10 +2976,10 @@
* @param info the subscriptionInfo to check against.
* @return true if this subscription should be visible to the API caller.
*
+ * @hide
*/
- private boolean isSubscriptionVisible(SubscriptionInfo info) {
+ public boolean isSubscriptionVisible(SubscriptionInfo info) {
if (info == null) return false;
-
// If subscription is NOT grouped opportunistic subscription, it's visible.
if (info.getGroupUuid() == null || !info.isOpportunistic()) return true;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 2ba34c6..d6011b9 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -105,6 +105,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.regex.Matcher;
@@ -9239,6 +9240,10 @@
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#getServiceStateForSubscriber", e);
+ } catch (NullPointerException e) {
+ AnomalyReporter.reportAnomaly(
+ UUID.fromString("a3ab0b9d-f2aa-4baf-911d-7096c0d4645a"),
+ "getServiceStateForSubscriber " + subId + " NPE");
}
return null;
}
@@ -11141,4 +11146,52 @@
}
return true;
}
+
+ /**
+ * Set allowing mobile data during voice call.
+ *
+ * @param allow {@code true} if allowing using data during voice call, {@code false} if
+ * disallowed
+ *
+ * @return {@code false} if the setting is changed.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean setDataAllowedDuringVoiceCall(boolean allow) {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.setDataAllowedDuringVoiceCall(getSubId(), allow);
+ }
+ } catch (RemoteException ex) {
+ if (!isSystemProcess()) {
+ ex.rethrowAsRuntimeException();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check whether data is allowed during voice call. Note this is for dual sim device that
+ * data might be disabled on non-default data subscription but explicitly turned on by settings.
+ *
+ * @return {@code true} if data is allowed during voice call.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isDataAllowedInVoiceCall() {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.isDataAllowedInVoiceCall(getSubId());
+ }
+ } catch (RemoteException ex) {
+ if (!isSystemProcess()) {
+ ex.rethrowAsRuntimeException();
+ }
+ }
+ return false;
+ }
}
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index a78bae4..165be64 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1310,6 +1310,9 @@
* @hide
*/
public static String getApnTypeString(int apnType) {
+ if (apnType == TYPE_ALL) {
+ return "*";
+ }
String apnTypeString = APN_TYPE_INT_MAP.get(apnType);
return apnTypeString == null ? "Unknown" : apnTypeString;
}
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index bb5c251..cde6db4 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -94,7 +94,7 @@
public static final int EVENT_ROAMING_SETTING_CHANGE = BASE + 48;
public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49;
public static final int EVENT_DEVICE_PROVISIONED_CHANGE = BASE + 50;
- public static final int EVENT_APN_WHITE_LIST_CHANGE = BASE + 51;
+ public static final int EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED = BASE + 51;
/***** Constants *****/
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index cf1323a..5a27a0f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1619,7 +1619,7 @@
* <p>
* See {@link UiccCardInfo} for more details on the kind of information available.
*
- * @param callingPackage package making the call, used to evaluate carrier privileges
+ * @param callingPackage package making the call, used to evaluate carrier privileges
* @return a list of UiccCardInfo objects, representing information on the currently inserted
* UICCs and eUICCs. Each UiccCardInfo in the list will have private information filtered out if
* the caller does not have adequate permissions for that card.
@@ -1996,4 +1996,15 @@
* Returns the MMS user agent profile URL.
*/
String getMmsUAProfUrl(int subId);
+
+ /**
+ * Set allowing mobile data during voice call.
+ */
+ boolean setDataAllowedDuringVoiceCall(int subId, boolean allow);
+
+ /**
+ * Check whether data is allowed during voice call. Note this is for dual sim device that
+ * data might be disabled on non-default data subscription but explicitly turned on by settings.
+ */
+ boolean isDataAllowedInVoiceCall(int subId);
}
diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java
index ef7c605..44dc24b 100644
--- a/telephony/java/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/java/com/android/internal/telephony/SmsApplication.java
@@ -465,7 +465,11 @@
int userId) {
TelephonyManager tm = (TelephonyManager)
context.getSystemService(Context.TELEPHONY_SERVICE);
- if (!tm.isSmsCapable()) {
+ RoleManager roleManager = (RoleManager) context.getSystemService(Context.ROLE_SERVICE);
+ // (b/134400042) RoleManager might be null in unit tests running older mockito versions
+ // that do not support mocking final classes.
+ if (!tm.isSmsCapable() && (roleManager == null || !roleManager.isRoleAvailable(
+ RoleManager.ROLE_SMS))) {
// No phone, no SMS
return null;
}
@@ -584,7 +588,11 @@
public static void setDefaultApplicationAsUser(String packageName, Context context,
int userId) {
TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
- if (!tm.isSmsCapable()) {
+ RoleManager roleManager = (RoleManager) context.getSystemService(Context.ROLE_SERVICE);
+ // (b/134400042) RoleManager might be null in unit tests running older mockito versions
+ // that do not support mocking final classes.
+ if (!tm.isSmsCapable() && (roleManager == null || !roleManager.isRoleAvailable(
+ RoleManager.ROLE_SMS))) {
// No phone, no SMS
return;
}