diff options
330 files changed, 11939 insertions, 5855 deletions
diff --git a/api/current.txt b/api/current.txt index b6ed1c7ed7aa..50d601d52105 100644 --- a/api/current.txt +++ b/api/current.txt @@ -7179,6 +7179,7 @@ package android.app.slice { field public static final java.lang.String HINT_ACTIONS = "actions"; field public static final java.lang.String HINT_CALLER_NEEDED = "caller_needed"; field public static final java.lang.String HINT_HORIZONTAL = "horizontal"; + field public static final java.lang.String HINT_KEY_WORDS = "key_words"; field public static final java.lang.String HINT_LARGE = "large"; field public static final java.lang.String HINT_LIST = "list"; field public static final java.lang.String HINT_LIST_ITEM = "list_item"; @@ -24345,7 +24346,6 @@ package android.media { method public abstract void prepareDrm(java.util.UUID) throws android.media.MediaPlayer2.ProvisioningNetworkErrorException, android.media.MediaPlayer2.ProvisioningServerErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException; method public abstract byte[] provideDrmKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer2.NoDrmSchemeException; method public abstract void releaseDrm() throws android.media.MediaPlayer2.NoDrmSchemeException; - method public abstract void reset(); method public abstract void restoreDrmKeys(byte[]) throws android.media.MediaPlayer2.NoDrmSchemeException; method public void seekTo(long); method public abstract void seekTo(long, int); @@ -24510,6 +24510,7 @@ package android.media { method public abstract void play(); method public abstract void prepare(); method public abstract void registerPlayerEventCallback(java.util.concurrent.Executor, android.media.MediaPlayerBase.PlayerEventCallback); + method public abstract void reset(); method public abstract void seekTo(long); method public abstract void setAudioAttributes(android.media.AudioAttributes); method public abstract void setDataSource(android.media.DataSourceDesc); @@ -42380,9 +42381,9 @@ package android.telephony { method public int getCid(); method public int getLac(); method public deprecated int getMcc(); - method public java.lang.String getMccStr(); + method public java.lang.String getMccString(); method public deprecated int getMnc(); - method public java.lang.String getMncStr(); + method public java.lang.String getMncString(); method public java.lang.String getMobileNetworkOperator(); method public java.lang.CharSequence getOperatorAlphaLong(); method public java.lang.CharSequence getOperatorAlphaShort(); @@ -42396,9 +42397,9 @@ package android.telephony { method public int getCi(); method public int getEarfcn(); method public deprecated int getMcc(); - method public java.lang.String getMccStr(); + method public java.lang.String getMccString(); method public deprecated int getMnc(); - method public java.lang.String getMncStr(); + method public java.lang.String getMncString(); method public java.lang.String getMobileNetworkOperator(); method public java.lang.CharSequence getOperatorAlphaLong(); method public java.lang.CharSequence getOperatorAlphaShort(); @@ -42412,8 +42413,8 @@ package android.telephony { method public int getCid(); method public int getCpid(); method public int getLac(); - method public java.lang.String getMccStr(); - method public java.lang.String getMncStr(); + method public java.lang.String getMccString(); + method public java.lang.String getMncString(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityTdscdma> CREATOR; } @@ -42422,9 +42423,9 @@ package android.telephony { method public int getCid(); method public int getLac(); method public deprecated int getMcc(); - method public java.lang.String getMccStr(); + method public java.lang.String getMccString(); method public deprecated int getMnc(); - method public java.lang.String getMncStr(); + method public java.lang.String getMncString(); method public java.lang.String getMobileNetworkOperator(); method public java.lang.CharSequence getOperatorAlphaLong(); method public java.lang.CharSequence getOperatorAlphaShort(); @@ -48822,6 +48823,7 @@ package android.view { method public static deprecated int getWindowTouchSlop(); method public static long getZoomControlsTimeout(); method public boolean hasPermanentMenuKey(); + method public boolean shouldShowMenuShortcutsWhenKeyboardPresent(); } public class ViewDebug { diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index 7f76ab14a171..8ecb538a1606 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -30,6 +30,7 @@ statsd_common_src := \ src/condition/condition_util.cpp \ src/condition/SimpleConditionTracker.cpp \ src/condition/ConditionWizard.cpp \ + src/condition/StateTracker.cpp \ src/config/ConfigKey.cpp \ src/config/ConfigListener.cpp \ src/config/ConfigManager.cpp \ @@ -69,7 +70,6 @@ statsd_common_src := \ src/subscriber/IncidentdReporter.cpp \ src/subscriber/SubscriberReporter.cpp \ src/HashableDimensionKey.cpp \ - src/guardrail/MemoryLeakTrackUtil.cpp \ src/guardrail/StatsdStats.cpp statsd_common_c_includes := \ @@ -102,8 +102,7 @@ statsd_common_shared_libraries := \ android.hardware.health@2.0 \ android.hardware.power@1.0 \ android.hardware.power@1.1 \ - android.hardware.thermal@1.0 \ - libmemunreachable + android.hardware.thermal@1.0 # ========= # statsd @@ -131,7 +130,7 @@ else LOCAL_CFLAGS += \ -Os endif -LOCAL_PROTOC_OPTIMIZE_TYPE := lite-static +LOCAL_PROTOC_OPTIMIZE_TYPE := lite LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes) LOCAL_C_INCLUDES += $(statsd_common_c_includes) @@ -189,6 +188,7 @@ LOCAL_SRC_FILES := \ tests/FieldValue_test.cpp \ tests/condition/CombinationConditionTracker_test.cpp \ tests/condition/SimpleConditionTracker_test.cpp \ + tests/condition/StateTracker_test.cpp \ tests/metrics/OringDurationTracker_test.cpp \ tests/metrics/MaxDurationTracker_test.cpp \ tests/metrics/CountMetricProducer_test.cpp \ diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h index b0e2c43e8cdd..621d0be9e853 100644 --- a/cmds/statsd/src/FieldValue.h +++ b/cmds/statsd/src/FieldValue.h @@ -41,6 +41,7 @@ int32_t encodeMatcherMask(int32_t mask[], int32_t depth); inline int32_t getSimpleField(size_t field) { return ((int32_t)field << 8 * 2); } + /** * Field is a wrapper class for 2 integers that represents the field of a log element in its Atom * proto. @@ -201,9 +202,9 @@ public: * } * * We translate the FieldMatcher into a Field, and mask - * First: [Matcher Field] 0x02010101 [Mask]0xffff7fff - * Last: [Matcher Field] 0x02018001 [Mask]0xffff80ff - * Any: [Matcher Field] 0x02010001 [Mask]0xffff00ff + * First: [Matcher Field] 0x02010101 [Mask]0xff7f7f7f + * Last: [Matcher Field] 0x02018001 [Mask]0xff7f807f + * Any: [Matcher Field] 0x02010001 [Mask]0xff7f007f * * [To match a log Field with a Matcher] we apply the bit mask to the log Field and check if * the result is equal to the Matcher Field. That's a bit wise AND operation + check if 2 ints are @@ -235,9 +236,17 @@ struct Matcher { inline bool operator!=(const Matcher& that) const { return mMatcher != that.getMatcher() || mMask != that.getMask(); - }; + } + + inline bool operator==(const Matcher& that) const { + return mMatcher == that.mMatcher && mMask == that.mMask; + } }; +inline Matcher getSimpleMatcher(int32_t tag, size_t field) { + return Matcher(Field(tag, getSimpleField(field)), 0xff7f0000); +} + /** * A wrapper for a union type to contain multiple types of values. * diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 9b58a14de0be..e98b54da5098 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -330,9 +330,7 @@ void StatsLogProcessor::flushIfNecessaryLocked( mLastByteSizeTimes[key] = timestampNs; if (totalBytes > StatsdStats::kMaxMetricsBytesPerConfig) { // Too late. We need to start clearing data. - // TODO(b/70571383): By 12/15/2017 add API to drop data directly - ProtoOutputStream proto; - metricsManager.onDumpReport(timestampNs, &proto); + metricsManager.dropData(timestampNs); StatsdStats::getInstance().noteDataDropped(key); VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str()); } else if (totalBytes > .9 * StatsdStats::kMaxMetricsBytesPerConfig) { diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 7a6aa1e26dc3..a672ab4fb314 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -113,7 +113,6 @@ private: FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize); FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast); FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge); - FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration1); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration2); FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensionsForSumDuration3); diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index ee4f434aa6a2..280081ec5a3d 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -22,7 +22,6 @@ #include "android-base/stringprintf.h" #include "config/ConfigKey.h" #include "config/ConfigManager.h" -#include "guardrail/MemoryLeakTrackUtil.h" #include "guardrail/StatsdStats.h" #include "storage/StorageManager.h" #include "subscriber/SubscriberReporter.h" @@ -638,9 +637,7 @@ status_t StatsService::cmd_remove_all_configs(FILE* out) { } status_t StatsService::cmd_dump_memory_info(FILE* out) { - std::string s = dumpMemInfo(100); - fprintf(out, "Memory Info\n"); - fprintf(out, "%s", s.c_str()); + fprintf(out, "meminfo not available.\n"); return NO_ERROR; } diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index 642604e17bf5..6ee51f38dde8 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -193,7 +193,8 @@ void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const MetricDim if (!mSubscriptions.empty()) { if (mAlert.has_id()) { - ALOGI("An anomaly (%lld) has occurred! Informing subscribers.", mAlert.id()); + ALOGI("An anomaly (%lld) %s has occurred! Informing subscribers.", mAlert.id(), + key.toString().c_str()); informSubscribers(key); } else { ALOGI("An anomaly (with no id) has occurred! Not informing any subscribers."); diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h index ba185f6661ec..7b8dc6bfd938 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.h +++ b/cmds/statsd/src/condition/CombinationConditionTracker.h @@ -52,6 +52,29 @@ public: const vector<Matcher>& dimensionFields, std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override; + // Only one child predicate can have dimension. + const std::set<HashableDimensionKey>* getChangedToTrueDimensions( + const std::vector<sp<ConditionTracker>>& allConditions) const override { + for (const auto& child : mChildren) { + auto result = allConditions[child]->getChangedToTrueDimensions(allConditions); + if (result != nullptr) { + return result; + } + } + return nullptr; + } + // Only one child predicate can have dimension. + const std::set<HashableDimensionKey>* getChangedToFalseDimensions( + const std::vector<sp<ConditionTracker>>& allConditions) const override { + for (const auto& child : mChildren) { + auto result = allConditions[child]->getChangedToFalseDimensions(allConditions); + if (result != nullptr) { + return result; + } + } + return nullptr; + } + private: LogicalOperation mLogicalOperation; diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h index 2612a9a06da1..856a3a0467ea 100644 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -111,6 +111,11 @@ public: return mSliced; } + virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions( + const std::vector<sp<ConditionTracker>>& allConditions) const = 0; + virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions( + const std::vector<sp<ConditionTracker>>& allConditions) const = 0; + protected: const int64_t mConditionId; diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp index c8722c362fe6..952b0cccf217 100644 --- a/cmds/statsd/src/condition/ConditionWizard.cpp +++ b/cmds/statsd/src/condition/ConditionWizard.cpp @@ -41,6 +41,16 @@ ConditionState ConditionWizard::getMetConditionDimension( *dimensionsKeySet); } +const set<HashableDimensionKey>* ConditionWizard::getChangedToTrueDimensions( + const int index) const { + return mAllConditions[index]->getChangedToTrueDimensions(mAllConditions); +} + +const set<HashableDimensionKey>* ConditionWizard::getChangedToFalseDimensions( + const int index) const { + return mAllConditions[index]->getChangedToFalseDimensions(mAllConditions); +} + } // namespace statsd } // namespace os } // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h index 4831d5622a3d..fcfdc2a5815b 100644 --- a/cmds/statsd/src/condition/ConditionWizard.h +++ b/cmds/statsd/src/condition/ConditionWizard.h @@ -47,6 +47,10 @@ public: const int index, const vector<Matcher>& dimensionFields, std::unordered_set<HashableDimensionKey>* dimensionsKeySet) const; + virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(const int index) const; + virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions( + const int index) const; + private: std::vector<sp<ConditionTracker>> mAllConditions; }; diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp index 624119f3ad04..9e27a8b51a99 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -20,8 +20,6 @@ #include "SimpleConditionTracker.h" #include "guardrail/StatsdStats.h" -#include <log/logprint.h> - namespace android { namespace os { namespace statsd { @@ -108,11 +106,20 @@ bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig, return mInitialized; } -void print(const map<HashableDimensionKey, int>& conditions, const int64_t& id) { - VLOG("%lld DUMP:", (long long)id); - for (const auto& pair : conditions) { +void SimpleConditionTracker::dumpState() { + VLOG("%lld DUMP:", (long long)mConditionId); + for (const auto& pair : mSlicedConditionState) { VLOG("\t%s : %d", pair.first.c_str(), pair.second); } + + VLOG("Changed to true keys: \n"); + for (const auto& key : mLastChangedToTrueDimensions) { + VLOG("%s", key.toString().c_str()); + } + VLOG("Changed to false keys: \n"); + for (const auto& key : mLastChangedToFalseDimensions) { + VLOG("%s", key.toString().c_str()); + } } void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditionCache, @@ -123,6 +130,12 @@ void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditio (mInitialValue == ConditionState::kFalse && mSlicedConditionState.empty()) ? false : true; + for (const auto& cond : mSlicedConditionState) { + if (cond.second > 0) { + mLastChangedToFalseDimensions.insert(cond.first); + } + } + // After StopAll, we know everything has stopped. From now on, default condition is false. mInitialValue = ConditionState::kFalse; mSlicedConditionState.clear(); @@ -166,10 +179,12 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou if (matchStart && mInitialValue != ConditionState::kTrue) { mSlicedConditionState.insert(std::make_pair(outputKey, 1)); changed = true; + mLastChangedToTrueDimensions.insert(outputKey); } else if (mInitialValue != ConditionState::kFalse) { // it's a stop and we don't have history about it. // If the default condition is not false, it means this stop is valuable to us. mSlicedConditionState.insert(std::make_pair(outputKey, 0)); + mLastChangedToFalseDimensions.insert(outputKey); changed = true; } } else { @@ -179,6 +194,7 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou newCondition = startedCount > 0 ? ConditionState::kTrue : ConditionState::kFalse; if (matchStart) { if (startedCount == 0) { + mLastChangedToTrueDimensions.insert(outputKey); // This condition for this output key will change from false -> true changed = true; } @@ -202,6 +218,7 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou } // if everything has stopped for this output key, condition true -> false; if (startedCount == 0) { + mLastChangedToFalseDimensions.insert(outputKey); changed = true; } } @@ -216,7 +233,7 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou // dump all dimensions for debugging if (DEBUG) { - print(mSlicedConditionState, mConditionId); + dumpState(); } (*conditionChangedCache) = changed; @@ -237,6 +254,8 @@ void SimpleConditionTracker::evaluateCondition( (long long)mConditionId, conditionCache[mIndex]); return; } + mLastChangedToTrueDimensions.clear(); + mLastChangedToFalseDimensions.clear(); if (mStopAllLogMatcherIndex >= 0 && mStopAllLogMatcherIndex < int(eventMatcherValues.size()) && eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) { @@ -297,14 +316,14 @@ void SimpleConditionTracker::evaluateCondition( // A high level assumption is that a predicate is either sliced or unsliced. We will never // have both sliced and unsliced version of a predicate. for (const HashableDimensionKey& outputValue : outputValues) { - // For sliced conditions, the value in the cache is not used. We don't need to update - // the overall condition state. - ConditionState tempState = ConditionState::kUnknown; + ConditionState tempState; bool tempChanged = false; handleConditionEvent(outputValue, matchedState == 1, &tempState, &tempChanged); if (tempChanged) { overallChanged = true; } + // ConditionState's | operator is overridden + overallState = overallState | tempState; } } conditionCache[mIndex] = overallState; diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h index c56512935297..e4b72b832215 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ b/cmds/statsd/src/condition/SimpleConditionTracker.h @@ -57,6 +57,23 @@ public: const vector<Matcher>& dimensionFields, std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override; + virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions( + const std::vector<sp<ConditionTracker>>& allConditions) const { + if (mSliced) { + return &mLastChangedToTrueDimensions; + } else { + return nullptr; + } + } + virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions( + const std::vector<sp<ConditionTracker>>& allConditions) const { + if (mSliced) { + return &mLastChangedToFalseDimensions; + } else { + return nullptr; + } + } + private: const ConfigKey mConfigKey; // The index of the LogEventMatcher which defines the start. @@ -75,6 +92,9 @@ private: std::vector<Matcher> mOutputDimensions; + std::set<HashableDimensionKey> mLastChangedToTrueDimensions; + std::set<HashableDimensionKey> mLastChangedToFalseDimensions; + int mDimensionTag; std::map<HashableDimensionKey, int> mSlicedConditionState; @@ -87,6 +107,8 @@ private: bool hitGuardRail(const HashableDimensionKey& newKey); + void dumpState(); + FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition); FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim); FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll); diff --git a/cmds/statsd/src/condition/StateTracker.cpp b/cmds/statsd/src/condition/StateTracker.cpp new file mode 100644 index 000000000000..e479f93d9a29 --- /dev/null +++ b/cmds/statsd/src/condition/StateTracker.cpp @@ -0,0 +1,233 @@ +/* + * 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. + */ +#define DEBUG false // STOPSHIP if true +#include "Log.h" + +#include "StateTracker.h" +#include "guardrail/StatsdStats.h" + +namespace android { +namespace os { +namespace statsd { + +using std::string; +using std::unordered_set; +using std::vector; + +StateTracker::StateTracker(const ConfigKey& key, const int64_t& id, const int index, + const SimplePredicate& simplePredicate, + const unordered_map<int64_t, int>& trackerNameIndexMap, + const vector<Matcher> primaryKeys) + : ConditionTracker(id, index), mConfigKey(key), mPrimaryKeys(primaryKeys) { + if (simplePredicate.has_start()) { + auto pair = trackerNameIndexMap.find(simplePredicate.start()); + if (pair == trackerNameIndexMap.end()) { + ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start()); + return; + } + mStartLogMatcherIndex = pair->second; + mTrackerIndex.insert(mStartLogMatcherIndex); + } else { + ALOGW("Condition %lld must have a start matcher", (long long)id); + return; + } + + if (simplePredicate.has_dimensions()) { + translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions); + if (mOutputDimensions.size() > 0) { + mSliced = true; + mDimensionTag = mOutputDimensions[0].mMatcher.getTag(); + } else { + ALOGW("Condition %lld has invalid dimensions", (long long)id); + return; + } + } else { + ALOGW("Condition %lld being a state tracker, but has no dimension", (long long)id); + return; + } + + if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) { + mInitialValue = ConditionState::kFalse; + } else { + mInitialValue = ConditionState::kUnknown; + } + + mNonSlicedConditionState = mInitialValue; + mInitialized = true; +} + +StateTracker::~StateTracker() { + VLOG("~StateTracker()"); +} + +bool StateTracker::init(const vector<Predicate>& allConditionConfig, + const vector<sp<ConditionTracker>>& allConditionTrackers, + const unordered_map<int64_t, int>& conditionIdIndexMap, + vector<bool>& stack) { + return mInitialized; +} + +void StateTracker::dumpState() { + VLOG("StateTracker %lld DUMP:", (long long)mConditionId); + for (const auto& value : mSlicedState) { + VLOG("\t%s -> %s", value.first.toString().c_str(), value.second.toString().c_str()); + } + VLOG("Last Changed to True: "); + for (const auto& value : mLastChangedToTrueDimensions) { + VLOG("%s", value.toString().c_str()); + } + VLOG("Last Changed to False: "); + for (const auto& value : mLastChangedToFalseDimensions) { + VLOG("%s", value.toString().c_str()); + } +} + +bool StateTracker::hitGuardRail(const HashableDimensionKey& newKey) { + if (mSlicedState.find(newKey) != mSlicedState.end()) { + // if the condition is not sliced or the key is not new, we are good! + return false; + } + // 1. Report the tuple count if the tuple count > soft limit + if (mSlicedState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { + size_t newTupleCount = mSlicedState.size() + 1; + StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mConditionId, newTupleCount); + // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. + if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { + ALOGE("Predicate %lld dropping data for dimension key %s", + (long long)mConditionId, newKey.c_str()); + return true; + } + } + return false; +} + +void StateTracker::evaluateCondition(const LogEvent& event, + const vector<MatchingState>& eventMatcherValues, + const vector<sp<ConditionTracker>>& mAllConditions, + vector<ConditionState>& conditionCache, + vector<bool>& conditionChangedCache) { + mLastChangedToTrueDimensions.clear(); + mLastChangedToFalseDimensions.clear(); + if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { + // it has been evaluated. + VLOG("Yes, already evaluated, %lld %d", (long long)mConditionId, conditionCache[mIndex]); + return; + } + + if (mStartLogMatcherIndex >= 0 && + eventMatcherValues[mStartLogMatcherIndex] != MatchingState::kMatched) { + conditionCache[mIndex] = + mSlicedState.size() > 0 ? ConditionState::kTrue : ConditionState::kFalse; + conditionChangedCache[mIndex] = false; + return; + } + + VLOG("StateTracker evaluate event %s", event.ToString().c_str()); + + vector<HashableDimensionKey> keys; + vector<HashableDimensionKey> outputs; + filterValues(mPrimaryKeys, event.getValues(), &keys); + filterValues(mOutputDimensions, event.getValues(), &outputs); + if (keys.size() != 1 || outputs.size() != 1) { + ALOGE("More than 1 states in the event?? panic now!"); + conditionCache[mIndex] = + mSlicedState.size() > 0 ? ConditionState::kTrue : ConditionState::kFalse; + conditionChangedCache[mIndex] = false; + return; + } + // Primary key can exclusive fields must be simple fields. so there won't be more than + // one keys matched. + const auto& primaryKey = keys[0]; + const auto& state = outputs[0]; + hitGuardRail(primaryKey); + + VLOG("StateTracker: key %s state %s", primaryKey.toString().c_str(), state.toString().c_str()); + + auto it = mSlicedState.find(primaryKey); + if (it == mSlicedState.end()) { + mSlicedState[primaryKey] = state; + conditionCache[mIndex] = ConditionState::kTrue; + mLastChangedToTrueDimensions.insert(state); + conditionChangedCache[mIndex] = true; + } else if (!(it->second == state)) { + mLastChangedToFalseDimensions.insert(it->second); + mLastChangedToTrueDimensions.insert(state); + mSlicedState[primaryKey] = state; + conditionCache[mIndex] = ConditionState::kTrue; + conditionChangedCache[mIndex] = true; + } else { + conditionCache[mIndex] = ConditionState::kTrue; + conditionChangedCache[mIndex] = false; + } + + if (DEBUG) { + dumpState(); + } + return; +} + +void StateTracker::isConditionMet( + const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions, + const vector<Matcher>& dimensionFields, vector<ConditionState>& conditionCache, + std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const { + if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { + // it has been evaluated. + VLOG("Yes, already evaluated, %lld %d", (long long)mConditionId, conditionCache[mIndex]); + return; + } + + const auto pair = conditionParameters.find(mConditionId); + if (pair == conditionParameters.end()) { + if (mSlicedState.size() > 0) { + conditionCache[mIndex] = ConditionState::kTrue; + + for (const auto& state : mSlicedState) { + dimensionsKeySet.insert(state.second); + } + } else { + conditionCache[mIndex] = ConditionState::kUnknown; + } + return; + } + + const auto& primaryKeys = pair->second; + conditionCache[mIndex] = mInitialValue; + for (const auto& primaryKey : primaryKeys) { + auto it = mSlicedState.find(primaryKey); + if (it != mSlicedState.end()) { + conditionCache[mIndex] = ConditionState::kTrue; + dimensionsKeySet.insert(it->second); + } + } +} + +ConditionState StateTracker::getMetConditionDimension( + const std::vector<sp<ConditionTracker>>& allConditions, + const vector<Matcher>& dimensionFields, + std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const { + if (mSlicedState.size() > 0) { + for (const auto& state : mSlicedState) { + dimensionsKeySet.insert(state.second); + } + return ConditionState::kTrue; + } + + return mInitialValue; +} + +} // namespace statsd +} // namespace os +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/condition/StateTracker.h b/cmds/statsd/src/condition/StateTracker.h new file mode 100644 index 000000000000..3fe6e60225d2 --- /dev/null +++ b/cmds/statsd/src/condition/StateTracker.h @@ -0,0 +1,110 @@ +/* + * 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. + */ +#pragma once + +#include <gtest/gtest_prod.h> +#include "ConditionTracker.h" +#include "config/ConfigKey.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "stats_util.h" + +namespace android { +namespace os { +namespace statsd { + +class StateTracker : public virtual ConditionTracker { +public: + StateTracker(const ConfigKey& key, const int64_t& id, const int index, + const SimplePredicate& simplePredicate, + const std::unordered_map<int64_t, int>& trackerNameIndexMap, + const vector<Matcher> primaryKeys); + + ~StateTracker(); + + bool init(const std::vector<Predicate>& allConditionConfig, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<int64_t, int>& conditionIdIndexMap, + std::vector<bool>& stack) override; + + void evaluateCondition(const LogEvent& event, + const std::vector<MatchingState>& eventMatcherValues, + const std::vector<sp<ConditionTracker>>& mAllConditions, + std::vector<ConditionState>& conditionCache, + std::vector<bool>& changedCache) override; + + /** + * Note: dimensionFields will be ignored in StateTracker, because we demand metrics + * must take the entire dimension fields from StateTracker. This is to make implementation + * simple and efficient. + * + * For example: wakelock duration by uid process states: + * dimension in condition must be {uid, process state}. + */ + void isConditionMet(const ConditionKey& conditionParameters, + const std::vector<sp<ConditionTracker>>& allConditions, + const vector<Matcher>& dimensionFields, + std::vector<ConditionState>& conditionCache, + std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override; + + /** + * Note: dimensionFields will be ignored in StateTracker, because we demand metrics + * must take the entire dimension fields from StateTracker. This is to make implementation + * simple and efficient. + */ + ConditionState getMetConditionDimension( + const std::vector<sp<ConditionTracker>>& allConditions, + const vector<Matcher>& dimensionFields, + std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override; + + virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions( + const std::vector<sp<ConditionTracker>>& allConditions) const { + return &mLastChangedToTrueDimensions; + } + + virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions( + const std::vector<sp<ConditionTracker>>& allConditions) const { + return &mLastChangedToFalseDimensions; + } + +private: + const ConfigKey mConfigKey; + + // The index of the LogEventMatcher which defines the start. + int mStartLogMatcherIndex; + + std::set<HashableDimensionKey> mLastChangedToTrueDimensions; + std::set<HashableDimensionKey> mLastChangedToFalseDimensions; + + std::vector<Matcher> mOutputDimensions; + std::vector<Matcher> mPrimaryKeys; + + ConditionState mInitialValue; + + int mDimensionTag; + + void dumpState(); + + bool hitGuardRail(const HashableDimensionKey& newKey); + + // maps from [primary_key] to [primary_key, exclusive_state]. + std::unordered_map<HashableDimensionKey, HashableDimensionKey> mSlicedState; + + FRIEND_TEST(StateTrackerTest, TestStateChange); +}; + +} // namespace statsd +} // namespace os +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/config/ConfigKey.cpp b/cmds/statsd/src/config/ConfigKey.cpp index d791f8632f3c..4a2bd2799df8 100644 --- a/cmds/statsd/src/config/ConfigKey.cpp +++ b/cmds/statsd/src/config/ConfigKey.cpp @@ -16,14 +16,10 @@ #include "config/ConfigKey.h" -#include <sstream> - namespace android { namespace os { namespace statsd { -using std::ostringstream; - ConfigKey::ConfigKey() { } @@ -37,9 +33,9 @@ ConfigKey::~ConfigKey() { } string ConfigKey::ToString() const { - ostringstream out; - out << '(' << mUid << ',' << mId << ')'; - return out.str(); + string s; + s += "(" + std::to_string(mUid) + " " + std::to_string(mId) + ")"; + return s; } diff --git a/cmds/statsd/src/config/ConfigKey.h b/cmds/statsd/src/config/ConfigKey.h index 3ad0eed3f2b9..dc79519d1f34 100644 --- a/cmds/statsd/src/config/ConfigKey.h +++ b/cmds/statsd/src/config/ConfigKey.h @@ -18,8 +18,6 @@ #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include <functional> -#include <iostream> #include <string> namespace android { @@ -27,7 +25,6 @@ namespace os { namespace statsd { using std::hash; -using std::ostream; using std::string; /** @@ -68,10 +65,6 @@ private: int mUid; }; -inline ostream& operator<<(ostream& os, const ConfigKey& config) { - return os << config.ToString(); -} - int64_t StrToInt64(const string& str); } // namespace statsd diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index 0881d44c96d4..22faaa184ffd 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -36,6 +36,7 @@ using android::util::FIELD_TYPE_STRING; using android::util::ProtoOutputStream; using std::lock_guard; using std::map; +using std::shared_ptr; using std::string; using std::vector; @@ -58,6 +59,38 @@ const int FIELD_ID_SUBSCRIBER_ALARMS_REGISTERED = 1; const int FIELD_ID_LOGGER_STATS_TIME = 1; const int FIELD_ID_LOGGER_STATS_ERROR_CODE = 2; +const int FIELD_ID_CONFIG_STATS_UID = 1; +const int FIELD_ID_CONFIG_STATS_ID = 2; +const int FIELD_ID_CONFIG_STATS_CREATION = 3; +const int FIELD_ID_CONFIG_STATS_DELETION = 4; +const int FIELD_ID_CONFIG_STATS_METRIC_COUNT = 5; +const int FIELD_ID_CONFIG_STATS_CONDITION_COUNT = 6; +const int FIELD_ID_CONFIG_STATS_MATCHER_COUNT = 7; +const int FIELD_ID_CONFIG_STATS_ALERT_COUNT = 8; +const int FIELD_ID_CONFIG_STATS_VALID = 9; +const int FIELD_ID_CONFIG_STATS_BROADCAST = 10; +const int FIELD_ID_CONFIG_STATS_DATA_DROP = 11; +const int FIELD_ID_CONFIG_STATS_DUMP_REPORT = 12; +const int FIELD_ID_CONFIG_STATS_MATCHER_STATS = 13; +const int FIELD_ID_CONFIG_STATS_CONDITION_STATS = 14; +const int FIELD_ID_CONFIG_STATS_METRIC_STATS = 15; +const int FIELD_ID_CONFIG_STATS_ALERT_STATS = 16; + +const int FIELD_ID_MATCHER_STATS_ID = 1; +const int FIELD_ID_MATCHER_STATS_COUNT = 2; +const int FIELD_ID_CONDITION_STATS_ID = 1; +const int FIELD_ID_CONDITION_STATS_COUNT = 2; +const int FIELD_ID_METRIC_STATS_ID = 1; +const int FIELD_ID_METRIC_STATS_COUNT = 2; +const int FIELD_ID_ALERT_STATS_ID = 1; +const int FIELD_ID_ALERT_STATS_COUNT = 2; + +const int FIELD_ID_UID_MAP_SNAPSHOTS = 1; +const int FIELD_ID_UID_MAP_CHANGES = 2; +const int FIELD_ID_UID_MAP_BYTES_USED = 3; +const int FIELD_ID_UID_MAP_DROPPED_SNAPSHOTS = 4; +const int FIELD_ID_UID_MAP_DROPPED_CHANGES = 5; + std::map<int, long> StatsdStats::kPullerCooldownMap = { {android::util::KERNEL_WAKELOCK, 1}, {android::util::WIFI_BYTES_TRANSFER, 1}, @@ -81,7 +114,7 @@ StatsdStats& StatsdStats::getInstance() { return statsInstance; } -void StatsdStats::addToIceBoxLocked(const StatsdStatsReport_ConfigStats& stats) { +void StatsdStats::addToIceBoxLocked(shared_ptr<ConfigStats>& stats) { // The size of mIceBox grows strictly by one at a time. It won't be > kMaxIceBoxSize. if (mIceBox.size() == kMaxIceBoxSize) { mIceBox.pop_front(); @@ -97,20 +130,20 @@ void StatsdStats::noteConfigReceived(const ConfigKey& key, int metricsCount, int // If there is an existing config for the same key, icebox the old config. noteConfigRemovedInternalLocked(key); - StatsdStatsReport_ConfigStats configStats; - configStats.set_uid(key.GetUid()); - configStats.set_id(key.GetId()); - configStats.set_creation_time_sec(nowTimeSec); - configStats.set_metric_count(metricsCount); - configStats.set_condition_count(conditionsCount); - configStats.set_matcher_count(matchersCount); - configStats.set_alert_count(alertsCount); - configStats.set_is_valid(isValid); + shared_ptr<ConfigStats> configStats = std::make_shared<ConfigStats>(); + configStats->uid = key.GetUid(); + configStats->id = key.GetId(); + configStats->creation_time_sec = nowTimeSec; + configStats->metric_count = metricsCount; + configStats->condition_count = conditionsCount; + configStats->matcher_count = matchersCount; + configStats->alert_count = alertsCount; + configStats->is_valid = isValid; if (isValid) { mConfigStats[key] = configStats; } else { - configStats.set_deletion_time_sec(nowTimeSec); + configStats->deletion_time_sec = nowTimeSec; addToIceBoxLocked(configStats); } } @@ -119,14 +152,7 @@ void StatsdStats::noteConfigRemovedInternalLocked(const ConfigKey& key) { auto it = mConfigStats.find(key); if (it != mConfigStats.end()) { int32_t nowTimeSec = getWallClockSec(); - it->second.set_deletion_time_sec(nowTimeSec); - // Add condition stats, metrics stats, matcher stats, alert stats - addSubStatsToConfigLocked(key, it->second); - // Remove them after they are added to the config stats. - mMatcherStats.erase(key); - mMetricsStats.erase(key); - mAlertStats.erase(key); - mConditionStats.erase(key); + it->second->deletion_time_sec = nowTimeSec; addToIceBoxLocked(it->second); mConfigStats.erase(it); } @@ -148,12 +174,10 @@ void StatsdStats::noteBroadcastSent(const ConfigKey& key, int32_t timeSec) { ALOGE("Config key %s not found!", key.ToString().c_str()); return; } - if (it->second.broadcast_sent_time_sec_size() >= kMaxTimestampCount) { - auto timestampList = it->second.mutable_broadcast_sent_time_sec(); - // This is O(N) operation. It shouldn't happen often, and N is only 20. - timestampList->erase(timestampList->begin()); + if (it->second->broadcast_sent_time_sec.size() == kMaxTimestampCount) { + it->second->broadcast_sent_time_sec.pop_front(); } - it->second.add_broadcast_sent_time_sec(timeSec); + it->second->broadcast_sent_time_sec.push_back(timeSec); } void StatsdStats::noteDataDropped(const ConfigKey& key) { @@ -167,12 +191,10 @@ void StatsdStats::noteDataDropped(const ConfigKey& key, int32_t timeSec) { ALOGE("Config key %s not found!", key.ToString().c_str()); return; } - if (it->second.data_drop_time_sec_size() >= kMaxTimestampCount) { - auto timestampList = it->second.mutable_data_drop_time_sec(); - // This is O(N) operation. It shouldn't happen often, and N is only 20. - timestampList->erase(timestampList->begin()); + if (it->second->data_drop_time_sec.size() == kMaxTimestampCount) { + it->second->data_drop_time_sec.pop_front(); } - it->second.add_data_drop_time_sec(timeSec); + it->second->data_drop_time_sec.push_back(timeSec); } void StatsdStats::noteMetricsReportSent(const ConfigKey& key) { @@ -186,39 +208,42 @@ void StatsdStats::noteMetricsReportSent(const ConfigKey& key, int32_t timeSec) { ALOGE("Config key %s not found!", key.ToString().c_str()); return; } - if (it->second.dump_report_time_sec_size() >= kMaxTimestampCount) { - auto timestampList = it->second.mutable_dump_report_time_sec(); - // This is O(N) operation. It shouldn't happen often, and N is only 20. - timestampList->erase(timestampList->begin()); + if (it->second->dump_report_time_sec.size() == kMaxTimestampCount) { + it->second->dump_report_time_sec.pop_front(); } - it->second.add_dump_report_time_sec(timeSec); + it->second->dump_report_time_sec.push_back(timeSec); } void StatsdStats::noteUidMapDropped(int snapshots, int deltas) { lock_guard<std::mutex> lock(mLock); - mUidMapStats.set_dropped_snapshots(mUidMapStats.dropped_snapshots() + snapshots); - mUidMapStats.set_dropped_changes(mUidMapStats.dropped_changes() + deltas); + mUidMapStats.dropped_snapshots += mUidMapStats.dropped_snapshots + snapshots; + mUidMapStats.dropped_changes += mUidMapStats.dropped_changes + deltas; } void StatsdStats::setUidMapSnapshots(int snapshots) { lock_guard<std::mutex> lock(mLock); - mUidMapStats.set_snapshots(snapshots); + mUidMapStats.snapshots = snapshots; } void StatsdStats::setUidMapChanges(int changes) { lock_guard<std::mutex> lock(mLock); - mUidMapStats.set_changes(changes); + mUidMapStats.changes = changes; } void StatsdStats::setCurrentUidMapMemory(int bytes) { lock_guard<std::mutex> lock(mLock); - mUidMapStats.set_bytes_used(bytes); + mUidMapStats.bytes_used = bytes; } void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size) { lock_guard<std::mutex> lock(mLock); // if name doesn't exist before, it will create the key with count 0. - auto& conditionSizeMap = mConditionStats[key]; + auto statsIt = mConfigStats.find(key); + if (statsIt == mConfigStats.end()) { + return; + } + + auto& conditionSizeMap = statsIt->second->condition_stats; if (size > conditionSizeMap[id]) { conditionSizeMap[id] = size; } @@ -227,7 +252,11 @@ void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const int64_t void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size) { lock_guard<std::mutex> lock(mLock); // if name doesn't exist before, it will create the key with count 0. - auto& metricsDimensionMap = mMetricsStats[key]; + auto statsIt = mConfigStats.find(key); + if (statsIt == mConfigStats.end()) { + return; + } + auto& metricsDimensionMap = statsIt->second->metric_stats; if (size > metricsDimensionMap[id]) { metricsDimensionMap[id] = size; } @@ -235,14 +264,21 @@ void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const int64_t& i void StatsdStats::noteMatcherMatched(const ConfigKey& key, const int64_t& id) { lock_guard<std::mutex> lock(mLock); - auto& matcherStats = mMatcherStats[key]; - matcherStats[id]++; + + auto statsIt = mConfigStats.find(key); + if (statsIt == mConfigStats.end()) { + return; + } + statsIt->second->matcher_stats[id]++; } void StatsdStats::noteAnomalyDeclared(const ConfigKey& key, const int64_t& id) { lock_guard<std::mutex> lock(mLock); - auto& alertStats = mAlertStats[key]; - alertStats[id]++; + auto statsIt = mConfigStats.find(key); + if (statsIt == mConfigStats.end()) { + return; + } + statsIt->second->alert_stats[id]++; } void StatsdStats::noteRegisteredAnomalyAlarmChanged() { @@ -299,69 +335,18 @@ void StatsdStats::resetInternalLocked() { // Reset the historical data, but keep the active ConfigStats mStartTimeSec = getWallClockSec(); mIceBox.clear(); - mConditionStats.clear(); - mMetricsStats.clear(); std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0); - mAlertStats.clear(); mAnomalyAlarmRegisteredStats = 0; mPeriodicAlarmRegisteredStats = 0; - mMatcherStats.clear(); mLoggerErrors.clear(); for (auto& config : mConfigStats) { - config.second.clear_broadcast_sent_time_sec(); - config.second.clear_data_drop_time_sec(); - config.second.clear_dump_report_time_sec(); - config.second.clear_matcher_stats(); - config.second.clear_condition_stats(); - config.second.clear_metric_stats(); - config.second.clear_alert_stats(); - } -} - -void StatsdStats::addSubStatsToConfigLocked(const ConfigKey& key, - StatsdStatsReport_ConfigStats& configStats) { - // Add matcher stats - if (mMatcherStats.find(key) != mMatcherStats.end()) { - const auto& matcherStats = mMatcherStats[key]; - for (const auto& stats : matcherStats) { - auto output = configStats.add_matcher_stats(); - output->set_id(stats.first); - output->set_matched_times(stats.second); - VLOG("matcher %lld matched %d times", - (long long)stats.first, stats.second); - } - } - // Add condition stats - if (mConditionStats.find(key) != mConditionStats.end()) { - const auto& conditionStats = mConditionStats[key]; - for (const auto& stats : conditionStats) { - auto output = configStats.add_condition_stats(); - output->set_id(stats.first); - output->set_max_tuple_counts(stats.second); - VLOG("condition %lld max output tuple size %d", - (long long)stats.first, stats.second); - } - } - // Add metrics stats - if (mMetricsStats.find(key) != mMetricsStats.end()) { - const auto& conditionStats = mMetricsStats[key]; - for (const auto& stats : conditionStats) { - auto output = configStats.add_metric_stats(); - output->set_id(stats.first); - output->set_max_tuple_counts(stats.second); - VLOG("metrics %lld max output tuple size %d", - (long long)stats.first, stats.second); - } - } - // Add anomaly detection alert stats - if (mAlertStats.find(key) != mAlertStats.end()) { - const auto& alertStats = mAlertStats[key]; - for (const auto& stats : alertStats) { - auto output = configStats.add_alert_stats(); - output->set_id(stats.first); - output->set_alerted_times(stats.second); - VLOG("alert %lld declared %d times", (long long)stats.first, stats.second); - } + config.second->broadcast_sent_time_sec.clear(); + config.second->data_drop_time_sec.clear(); + config.second->dump_report_time_sec.clear(); + config.second->matcher_stats.clear(); + config.second->condition_stats.clear(); + config.second->metric_stats.clear(); + config.second->alert_stats.clear(); } } @@ -377,78 +362,57 @@ void StatsdStats::dumpStats(FILE* out) const { fprintf(out, "Config {%d_%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " "#matcher=%d, #alert=%d, valid=%d\n", - configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(), - configStats.deletion_time_sec(), configStats.metric_count(), - configStats.condition_count(), configStats.matcher_count(), - configStats.alert_count(), configStats.is_valid()); + configStats->uid, (long long)configStats->id, configStats->creation_time_sec, + configStats->deletion_time_sec, configStats->metric_count, + configStats->condition_count, configStats->matcher_count, configStats->alert_count, + configStats->is_valid); - for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) { + for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) { fprintf(out, "\tbroadcast time: %d\n", broadcastTime); } - for (const auto& dataDropTime : configStats.data_drop_time_sec()) { + for (const auto& dataDropTime : configStats->data_drop_time_sec) { fprintf(out, "\tdata drop time: %d\n", dataDropTime); } } fprintf(out, "%lu Active Configs\n", (unsigned long)mConfigStats.size()); for (auto& pair : mConfigStats) { - auto& key = pair.first; auto& configStats = pair.second; - fprintf(out, "Config {%d-%lld}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " "#matcher=%d, #alert=%d, valid=%d\n", - configStats.uid(), (long long)configStats.id(), configStats.creation_time_sec(), - configStats.deletion_time_sec(), configStats.metric_count(), - configStats.condition_count(), configStats.matcher_count(), - configStats.alert_count(), configStats.is_valid()); - for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) { + configStats->uid, (long long)configStats->id, configStats->creation_time_sec, + configStats->deletion_time_sec, configStats->metric_count, + configStats->condition_count, configStats->matcher_count, configStats->alert_count, + configStats->is_valid); + for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) { fprintf(out, "\tbroadcast time: %d\n", broadcastTime); } - for (const auto& dataDropTime : configStats.data_drop_time_sec()) { + for (const auto& dataDropTime : configStats->data_drop_time_sec) { fprintf(out, "\tdata drop time: %d\n", dataDropTime); } - for (const auto& dumpTime : configStats.dump_report_time_sec()) { + for (const auto& dumpTime : configStats->dump_report_time_sec) { fprintf(out, "\tdump report time: %d\n", dumpTime); } - // Add matcher stats - auto matcherIt = mMatcherStats.find(key); - if (matcherIt != mMatcherStats.end()) { - const auto& matcherStats = matcherIt->second; - for (const auto& stats : matcherStats) { - fprintf(out, "matcher %lld matched %d times\n", (long long)stats.first, - stats.second); - } + for (const auto& stats : pair.second->matcher_stats) { + fprintf(out, "matcher %lld matched %d times\n", (long long)stats.first, stats.second); } - // Add condition stats - auto conditionIt = mConditionStats.find(key); - if (conditionIt != mConditionStats.end()) { - const auto& conditionStats = conditionIt->second; - for (const auto& stats : conditionStats) { - fprintf(out, "condition %lld max output tuple size %d\n", (long long)stats.first, - stats.second); - } + + for (const auto& stats : pair.second->condition_stats) { + fprintf(out, "condition %lld max output tuple size %d\n", (long long)stats.first, + stats.second); } - // Add metrics stats - auto metricIt = mMetricsStats.find(key); - if (metricIt != mMetricsStats.end()) { - const auto& conditionStats = metricIt->second; - for (const auto& stats : conditionStats) { - fprintf(out, "metrics %lld max output tuple size %d\n", (long long)stats.first, - stats.second); - } + + for (const auto& stats : pair.second->condition_stats) { + fprintf(out, "metrics %lld max output tuple size %d\n", (long long)stats.first, + stats.second); } - // Add anomaly detection alert stats - auto alertIt = mAlertStats.find(key); - if (alertIt != mAlertStats.end()) { - const auto& alertStats = alertIt->second; - for (const auto& stats : alertStats) { - fprintf(out, "alert %lld declared %d times\n", (long long)stats.first, - stats.second); - } + + for (const auto& stats : pair.second->alert_stats) { + fprintf(out, "alert %lld declared %d times\n", (long long)stats.first, stats.second); } } fprintf(out, "********Pushed Atom stats***********\n"); @@ -462,7 +426,7 @@ void StatsdStats::dumpStats(FILE* out) const { fprintf(out, "********Pulled Atom stats***********\n"); for (const auto& pair : mPulledAtomStats) { fprintf(out, "Atom %d->%ld, %ld, %ld\n", (int)pair.first, (long)pair.second.totalPull, - (long)pair.second.totalPullFromCache, (long)pair.second.minPullIntervalSec); + (long)pair.second.totalPullFromCache, (long)pair.second.minPullIntervalSec); } if (mAnomalyAlarmRegisteredStats > 0) { @@ -478,8 +442,8 @@ void StatsdStats::dumpStats(FILE* out) const { fprintf(out, "UID map stats: bytes=%d, snapshots=%d, changes=%d, snapshots lost=%d, changes " "lost=%d\n", - mUidMapStats.bytes_used(), mUidMapStats.snapshots(), mUidMapStats.changes(), - mUidMapStats.dropped_snapshots(), mUidMapStats.dropped_changes()); + mUidMapStats.bytes_used, mUidMapStats.snapshots, mUidMapStats.changes, + mUidMapStats.dropped_snapshots, mUidMapStats.dropped_changes); for (const auto& error : mLoggerErrors) { time_t error_time = error.first; @@ -490,6 +454,73 @@ void StatsdStats::dumpStats(FILE* out) const { } } +void addConfigStatsToProto(const ConfigStats& configStats, ProtoOutputStream* proto) { + long long token = + proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_UID, configStats.uid); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_ID, (long long)configStats.id); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_CREATION, configStats.creation_time_sec); + if (configStats.deletion_time_sec != 0) { + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DELETION, + configStats.deletion_time_sec); + } + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_METRIC_COUNT, configStats.metric_count); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_CONDITION_COUNT, + configStats.condition_count); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_MATCHER_COUNT, configStats.matcher_count); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_ALERT_COUNT, configStats.alert_count); + proto->write(FIELD_TYPE_BOOL | FIELD_ID_CONFIG_STATS_VALID, configStats.is_valid); + + for (const auto& broadcast : configStats.broadcast_sent_time_sec) { + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_BROADCAST | FIELD_COUNT_REPEATED, + broadcast); + } + + for (const auto& drop : configStats.data_drop_time_sec) { + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DATA_DROP | FIELD_COUNT_REPEATED, + drop); + } + + for (const auto& dump : configStats.dump_report_time_sec) { + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DUMP_REPORT | FIELD_COUNT_REPEATED, + dump); + } + + for (const auto& pair : configStats.matcher_stats) { + long long tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_CONFIG_STATS_MATCHER_STATS); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_MATCHER_STATS_ID, (long long)pair.first); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_MATCHER_STATS_COUNT, pair.second); + proto->end(tmpToken); + } + + for (const auto& pair : configStats.condition_stats) { + long long tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_CONFIG_STATS_CONDITION_STATS); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_STATS_ID, (long long)pair.first); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONDITION_STATS_COUNT, pair.second); + proto->end(tmpToken); + } + + for (const auto& pair : configStats.metric_stats) { + long long tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_CONFIG_STATS_METRIC_STATS); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_STATS_ID, (long long)pair.first); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_STATS_COUNT, pair.second); + proto->end(tmpToken); + } + + for (const auto& pair : configStats.alert_stats) { + long long tmpToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_CONFIG_STATS_ALERT_STATS); + proto->write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_STATS_ID, (long long)pair.first); + proto->write(FIELD_TYPE_INT32 | FIELD_ID_ALERT_STATS_COUNT, pair.second); + proto->end(tmpToken); + } + + proto->end(token); +} + void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { lock_guard<std::mutex> lock(mLock); @@ -498,28 +529,11 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { proto.write(FIELD_TYPE_INT32 | FIELD_ID_END_TIME, (int32_t)getWallClockSec()); for (const auto& configStats : mIceBox) { - const int numBytes = configStats.ByteSize(); - vector<char> buffer(numBytes); - configStats.SerializeToArray(&buffer[0], numBytes); - proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS, &buffer[0], - buffer.size()); + addConfigStatsToProto(*configStats, &proto); } for (auto& pair : mConfigStats) { - auto& configStats = pair.second; - addSubStatsToConfigLocked(pair.first, configStats); - - const int numBytes = configStats.ByteSize(); - vector<char> buffer(numBytes); - configStats.SerializeToArray(&buffer[0], numBytes); - proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS, &buffer[0], - buffer.size()); - // reset the sub stats, the source of truth is in the individual map - // they will be repopulated when dumpStats() is called again. - configStats.clear_matcher_stats(); - configStats.clear_condition_stats(); - configStats.clear_metric_stats(); - configStats.clear_alert_stats(); + addConfigStatsToProto(*(pair.second), &proto); } const size_t atomCounts = mPushedAtomStats.size(); @@ -551,10 +565,14 @@ void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { proto.end(token); } - const int numBytes = mUidMapStats.ByteSize(); - vector<char> buffer(numBytes); - mUidMapStats.SerializeToArray(&buffer[0], numBytes); - proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UIDMAP_STATS, &buffer[0], buffer.size()); + long long uidMapToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_UIDMAP_STATS); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_SNAPSHOTS, mUidMapStats.snapshots); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_CHANGES, mUidMapStats.changes); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_BYTES_USED, mUidMapStats.bytes_used); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_DROPPED_SNAPSHOTS, + mUidMapStats.dropped_snapshots); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_DROPPED_CHANGES, mUidMapStats.dropped_changes); + proto.end(uidMapToken); for (const auto& error : mLoggerErrors) { long long token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_LOGGER_ERROR_STATS | diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index 24ac6883f444..c3f401324454 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -30,8 +30,49 @@ namespace android { namespace os { namespace statsd { +struct ConfigStats { + int32_t uid; + int64_t id; + int32_t creation_time_sec; + int32_t deletion_time_sec = 0; + int32_t metric_count; + int32_t condition_count; + int32_t matcher_count; + int32_t alert_count; + bool is_valid; + + std::list<int32_t> broadcast_sent_time_sec; + std::list<int32_t> data_drop_time_sec; + std::list<int32_t> dump_report_time_sec; + + // Stores how many times a matcher have been matched. The map size is capped by kMaxConfigCount. + std::map<const int64_t, int> matcher_stats; + + // Stores the number of output tuple of condition trackers when it's bigger than + // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1, + // it means some data has been dropped. The map size is capped by kMaxConfigCount. + std::map<const int64_t, int> condition_stats; + + // Stores the number of output tuple of metric producers when it's bigger than + // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1, + // it means some data has been dropped. The map size is capped by kMaxConfigCount. + std::map<const int64_t, int> metric_stats; + + // Stores the number of times an anomaly detection alert has been declared. + // The map size is capped by kMaxConfigCount. + std::map<const int64_t, int> alert_stats; +}; + +struct UidMapStats { + int32_t snapshots; + int32_t changes; + int32_t bytes_used; + int32_t dropped_snapshots; + int32_t dropped_changes; +}; + // Keeps track of stats of statsd. -// Single instance shared across the process. All methods are thread safe. +// Single instance shared across the process. All public methods are thread safe. class StatsdStats { public: static StatsdStats& getInstance(); @@ -233,25 +274,15 @@ private: int32_t mStartTimeSec; // Track the number of dropped entries used by the uid map. - StatsdStatsReport_UidMapStats mUidMapStats; + UidMapStats mUidMapStats; // The stats about the configs that are still in use. // The map size is capped by kMaxConfigCount. - std::map<const ConfigKey, StatsdStatsReport_ConfigStats> mConfigStats; + std::map<const ConfigKey, std::shared_ptr<ConfigStats>> mConfigStats; // Stores the stats for the configs that are no longer in use. // The size of the vector is capped by kMaxIceBoxSize. - std::list<const StatsdStatsReport_ConfigStats> mIceBox; - - // Stores the number of output tuple of condition trackers when it's bigger than - // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1, - // it means some data has been dropped. The map size is capped by kMaxConfigCount. - std::map<const ConfigKey, std::map<const int64_t, int>> mConditionStats; - - // Stores the number of output tuple of metric producers when it's bigger than - // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1, - // it means some data has been dropped. The map size is capped by kMaxConfigCount. - std::map<const ConfigKey, std::map<const int64_t, int>> mMetricsStats; + std::list<const std::shared_ptr<ConfigStats>> mIceBox; // Stores the number of times a pushed atom is logged. // The size of the vector is the largest pushed atom id in atoms.proto + 1. Atoms @@ -272,27 +303,18 @@ private: // Stores the number of times statsd registers the periodic alarm changes int mPeriodicAlarmRegisteredStats = 0; - // Stores the number of times an anomaly detection alert has been declared - // (per config, per alert name). The map size is capped by kMaxConfigCount. - std::map<const ConfigKey, std::map<const int64_t, int>> mAlertStats; - - // Stores how many times a matcher have been matched. The map size is capped by kMaxConfigCount. - std::map<const ConfigKey, std::map<const int64_t, int>> mMatcherStats; void noteConfigRemovedInternalLocked(const ConfigKey& key); void resetInternalLocked(); - void addSubStatsToConfigLocked(const ConfigKey& key, - StatsdStatsReport_ConfigStats& configStats); - void noteDataDropped(const ConfigKey& key, int32_t timeSec); void noteMetricsReportSent(const ConfigKey& key, int32_t timeSec); void noteBroadcastSent(const ConfigKey& key, int32_t timeSec); - void addToIceBoxLocked(const StatsdStatsReport_ConfigStats& stats); + void addToIceBoxLocked(std::shared_ptr<ConfigStats>& stats); FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd); FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd); diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index d282b86f52bd..4e4f146d27ac 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -17,8 +17,6 @@ #define DEBUG false // STOPSHIP if true #include "logd/LogEvent.h" -#include <sstream> - #include "stats_log_util.h" namespace android { @@ -27,7 +25,6 @@ namespace statsd { using namespace android::util; using android::util::ProtoOutputStream; -using std::ostringstream; using std::string; using std::vector; @@ -381,16 +378,15 @@ float LogEvent::GetFloat(size_t key, status_t* err) const { } string LogEvent::ToString() const { - ostringstream result; - result << "{ " << mLogdTimestampNs << " " << mElapsedTimestampNs << " (" << mTagId << ")"; + string result; + result += StringPrintf("{ %lld %lld (%d)", (long long)mLogdTimestampNs, + (long long)mElapsedTimestampNs, mTagId); for (const auto& value : mValues) { - result << StringPrintf("%#x", value.mField.getField()); - result << "->"; - result << value.mValue.toString(); - result << " "; + result += + StringPrintf("%#x", value.mField.getField()) + "->" + value.mValue.toString() + " "; } - result << " }"; - return result.str(); + result += " }"; + return result; } void LogEvent::ToProto(ProtoOutputStream& protoOutput) const { diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp index 5d5e64e48e54..364d4e9d5c94 100644 --- a/cmds/statsd/src/matchers/matcher_util.cpp +++ b/cmds/statsd/src/matchers/matcher_util.cpp @@ -21,7 +21,6 @@ #include "matchers/matcher_util.h" #include "stats_util.h" -using std::ostringstream; using std::set; using std::string; using std::unordered_map; diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index af2e362368c3..671c57f13e8a 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -151,8 +151,11 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, protoOutput->end(protoToken); mPastBuckets.clear(); +} - // TODO: Clear mDimensionKeyMap once the report is dumped. +void CountMetricProducer::dropDataLocked(const uint64_t dropTimeNs) { + flushIfNeededLocked(dropTimeNs); + mPastBuckets.clear(); } void CountMetricProducer::onConditionChangedLocked(const bool conditionMet, diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index 0c4291d5dfc2..1d8e42be635c 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -69,6 +69,8 @@ private: void dumpStatesLocked(FILE* out, bool verbose) const override{}; + void dropDataLocked(const uint64_t dropTimeNs) override; + // Util function to flush the old packet. void flushIfNeededLocked(const uint64_t& newEventTime) override; diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 80329c3a8a42..ac39662070d9 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -221,6 +221,11 @@ void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet, } } +void DurationMetricProducer::dropDataLocked(const uint64_t dropTimeNs) { + flushIfNeededLocked(dropTimeNs); + mPastBuckets.clear(); +} + void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { flushIfNeededLocked(dumpTimeNs); diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 73d074f1a2ce..23408a76998e 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -74,6 +74,8 @@ private: void dumpStatesLocked(FILE* out, bool verbose) const override; + void dropDataLocked(const uint64_t dropTimeNs) override; + // Util function to flush the old packet. void flushIfNeededLocked(const uint64_t& eventTime); diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index 96d0cfcc5897..fff91f67241c 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -75,6 +75,10 @@ EventMetricProducer::~EventMetricProducer() { VLOG("~EventMetricProducer() called"); } +void EventMetricProducer::dropDataLocked(const uint64_t dropTimeNs) { + mProto->clear(); +} + void EventMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) { } diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index 3f2c5a54e022..d6f81fda6ff6 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -55,6 +55,8 @@ private: // Internal interface to handle sliced condition change. void onSlicedConditionMayChangeLocked(const uint64_t eventTime) override; + void dropDataLocked(const uint64_t dropTimeNs) override; + // Internal function to calculate the current used bytes. size_t byteSizeLocked() const override; diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index 7d09ff9ffc8e..000c7a77d2a3 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -353,6 +353,11 @@ void GaugeMetricProducer::updateCurrentSlicedBucketForAnomaly() { } } +void GaugeMetricProducer::dropDataLocked(const uint64_t dropTimeNs) { + flushIfNeededLocked(dropTimeNs); + mPastBuckets.clear(); +} + // When a new matched event comes in, we check if event falls into the current // bucket. If not, flush the old counter to past buckets and initialize the new // bucket. diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index c3ae6ce921d2..ca8dc7582680 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -108,6 +108,8 @@ private: void dumpStatesLocked(FILE* out, bool verbose) const override{}; + void dropDataLocked(const uint64_t dropTimeNs) override; + // Util function to flush the old packet. void flushIfNeededLocked(const uint64_t& eventTime) override; diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 2bf624160475..05b7f8766983 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -148,6 +148,15 @@ public: return mMetricId; } + // Let MetricProducer drop in-memory data to save memory. + // We still need to keep future data valid and anomaly tracking work, which means we will + // have to flush old data, informing anomaly trackers then safely drop old data. + // We still keep current bucket data for future metrics' validity. + void dropData(const uint64_t dropTimeNs) { + std::lock_guard<std::mutex> lock(mMutex); + dropDataLocked(dropTimeNs); + } + protected: virtual void onConditionChangedLocked(const bool condition, const uint64_t eventTime) = 0; virtual void onSlicedConditionMayChangeLocked(const uint64_t eventTime) = 0; @@ -179,6 +188,8 @@ protected: return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs; } + virtual void dropDataLocked(const uint64_t dropTimeNs) = 0; + const int64_t mMetricId; const ConfigKey mConfigKey; diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 4c8a7d8a92d5..06930310bc3f 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -173,6 +173,12 @@ void MetricsManager::dumpStates(FILE* out, bool verbose) { } } +void MetricsManager::dropData(const uint64_t dropTimeNs) { + for (const auto& producer : mAllMetricProducers) { + producer->dropData(dropTimeNs); + } +} + void MetricsManager::onDumpReport(const uint64_t dumpTimeStampNs, ProtoOutputStream* protoOutput) { VLOG("=========================Metric Reports Start=========================="); // one StatsLogReport per MetricProduer diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index b50ef4a0c5a2..a32e037b5acd 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -73,6 +73,8 @@ public: return mLastReportTimeNs; }; + virtual void dropData(const uint64_t dropTimeNs); + // Config source owner can call onDumpReport() to get all the metrics collected. virtual void onDumpReport(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 35fcdc48a08a..f6bba3d9bb0d 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -129,6 +129,11 @@ void ValueMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventT VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); } +void ValueMetricProducer::dropDataLocked(const uint64_t dropTimeNs) { + flushIfNeededLocked(dropTimeNs); + mPastBuckets.clear(); +} + void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { VLOG("metric %lld dump report now...", (long long)mMetricId); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index b518f2f841cf..5e42bd255df4 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -106,6 +106,8 @@ private: void flushCurrentBucketLocked(const uint64_t& eventTimeNs) override; + void dropDataLocked(const uint64_t dropTimeNs) override; + const FieldMatcher mValueField; std::shared_ptr<StatsPullerManager> mStatsPullerManager; diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 9912afa01c3d..b5afef2502f8 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -21,6 +21,7 @@ #include "../condition/CombinationConditionTracker.h" #include "../condition/SimpleConditionTracker.h" +#include "../condition/StateTracker.h" #include "../external/StatsPullerManager.h" #include "../matchers/CombinationLogMatchingTracker.h" #include "../matchers/SimpleLogMatchingTracker.h" @@ -31,6 +32,7 @@ #include "../metrics/ValueMetricProducer.h" #include "stats_util.h" +#include "statslog.h" using std::set; using std::string; @@ -157,6 +159,49 @@ bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap, return true; } +/** + * A StateTracker is built from a SimplePredicate which has only "start", and no "stop" + * or "stop_all". The start must be an atom matcher that matches a state atom. It must + * have dimension, the dimension must be the state atom's primary fields plus exclusive state + * field. For example, the StateTracker is used in tracking UidProcessState and ScreenState. + * + */ +bool isStateTracker(const SimplePredicate& simplePredicate, vector<Matcher>* primaryKeys) { + // 1. must not have "stop". must have "dimension" + if (!simplePredicate.has_stop() && simplePredicate.has_dimensions()) { + // TODO: need to check the start atom matcher too. + auto it = android::util::kStateAtomsFieldOptions.find(simplePredicate.dimensions().field()); + // 2. must be based on a state atom. + if (it != android::util::kStateAtomsFieldOptions.end()) { + // 3. dimension must be primary fields + state field IN ORDER + size_t expectedDimensionCount = it->second.primaryFields.size() + 1; + vector<Matcher> dimensions; + translateFieldMatcher(simplePredicate.dimensions(), &dimensions); + if (dimensions.size() != expectedDimensionCount) { + return false; + } + // 3.1 check the primary fields first. + size_t index = 0; + for (const auto& field : it->second.primaryFields) { + Matcher matcher = getSimpleMatcher(it->first, field); + if (!(matcher == dimensions[index])) { + return false; + } + primaryKeys->push_back(matcher); + index++; + } + Matcher stateFieldMatcher = + getSimpleMatcher(it->first, it->second.exclusiveField); + // 3.2 last dimension should be the exclusive field. + if (!(dimensions.back() == stateFieldMatcher)) { + return false; + } + return true; + } + } + return false; +} // namespace statsd + bool initConditions(const ConfigKey& key, const StatsdConfig& config, const unordered_map<int64_t, int>& logTrackerMap, unordered_map<int64_t, int>& conditionTrackerMap, @@ -172,8 +217,16 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, int index = allConditionTrackers.size(); switch (condition.contents_case()) { case Predicate::ContentsCase::kSimplePredicate: { - allConditionTrackers.push_back(new SimpleConditionTracker( - key, condition.id(), index, condition.simple_predicate(), logTrackerMap)); + vector<Matcher> primaryKeys; + if (isStateTracker(condition.simple_predicate(), &primaryKeys)) { + allConditionTrackers.push_back(new StateTracker(key, condition.id(), index, + condition.simple_predicate(), + logTrackerMap, primaryKeys)); + } else { + allConditionTrackers.push_back(new SimpleConditionTracker( + key, condition.id(), index, condition.simple_predicate(), + logTrackerMap)); + } break; } case Predicate::ContentsCase::kCombination: { diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h index edda53d5e0cf..386de0b84aa0 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/metrics_manager_util.h @@ -110,6 +110,8 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, std::unordered_map<int, std::vector<int>>& trackerToConditionMap, std::set<int64_t> &noReportMetricIds); +bool isStateTracker(const SimplePredicate& simplePredicate, std::vector<Matcher>* primaryKeys); + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 269f25b30c84..3c5f5a2b9907 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -165,3 +165,86 @@ message ConfigMetricsReportList { repeated ConfigMetricsReport reports = 2; } + +message StatsdStatsReport { + optional int32 stats_begin_time_sec = 1; + + optional int32 stats_end_time_sec = 2; + + message MatcherStats { + optional int64 id = 1; + optional int32 matched_times = 2; + } + + message ConditionStats { + optional int64 id = 1; + optional int32 max_tuple_counts = 2; + } + + message MetricStats { + optional int64 id = 1; + optional int32 max_tuple_counts = 2; + } + + message AlertStats { + optional int64 id = 1; + optional int32 alerted_times = 2; + } + + message ConfigStats { + optional int32 uid = 1; + optional int64 id = 2; + optional int32 creation_time_sec = 3; + optional int32 deletion_time_sec = 4; + optional int32 metric_count = 5; + optional int32 condition_count = 6; + optional int32 matcher_count = 7; + optional int32 alert_count = 8; + optional bool is_valid = 9; + + repeated int32 broadcast_sent_time_sec = 10; + repeated int32 data_drop_time_sec = 11; + repeated int32 dump_report_time_sec = 12; + repeated MatcherStats matcher_stats = 13; + repeated ConditionStats condition_stats = 14; + repeated MetricStats metric_stats = 15; + repeated AlertStats alert_stats = 16; + } + + repeated ConfigStats config_stats = 3; + + message AtomStats { + optional int32 tag = 1; + optional int32 count = 2; + } + + repeated AtomStats atom_stats = 7; + + message UidMapStats { + optional int32 snapshots = 1; + optional int32 changes = 2; + optional int32 bytes_used = 3; + optional int32 dropped_snapshots = 4; + optional int32 dropped_changes = 5; + } + optional UidMapStats uidmap_stats = 8; + + message AnomalyAlarmStats { + optional int32 alarms_registered = 1; + } + optional AnomalyAlarmStats anomaly_alarm_stats = 9; + + message PulledAtomStats { + optional int32 atom_id = 1; + optional int64 total_pull = 2; + optional int64 total_pull_from_cache = 3; + optional int64 min_pull_interval_sec = 4; + } + repeated PulledAtomStats pulled_atom_stats = 10; + + message LoggerErrorStats { + optional int32 logger_disconnection_sec = 1; + optional int32 error_code = 2; + } + repeated LoggerErrorStats logger_error_stats = 11; +} diff --git a/cmds/statsd/src/stats_log_common.proto b/cmds/statsd/src/stats_log_common.proto index aeecd23c65f4..c41f31ebaa3f 100644 --- a/cmds/statsd/src/stats_log_common.proto +++ b/cmds/statsd/src/stats_log_common.proto @@ -46,87 +46,4 @@ message UidMapping { optional int64 version = 5; } repeated Change changes = 2; -} - -message StatsdStatsReport { - optional int32 stats_begin_time_sec = 1; - - optional int32 stats_end_time_sec = 2; - - message MatcherStats { - optional int64 id = 1; - optional int32 matched_times = 2; - } - - message ConditionStats { - optional int64 id = 1; - optional int32 max_tuple_counts = 2; - } - - message MetricStats { - optional int64 id = 1; - optional int32 max_tuple_counts = 2; - } - - message AlertStats { - optional int64 id = 1; - optional int32 alerted_times = 2; - } - - message ConfigStats { - optional int32 uid = 1; - optional int64 id = 2; - optional int32 creation_time_sec = 3; - optional int32 deletion_time_sec = 4; - optional int32 metric_count = 5; - optional int32 condition_count = 6; - optional int32 matcher_count = 7; - optional int32 alert_count = 8; - optional bool is_valid = 9; - - repeated int32 broadcast_sent_time_sec = 10; - repeated int32 data_drop_time_sec = 11; - repeated int32 dump_report_time_sec = 12; - repeated MatcherStats matcher_stats = 13; - repeated ConditionStats condition_stats = 14; - repeated MetricStats metric_stats = 15; - repeated AlertStats alert_stats = 16; - } - - repeated ConfigStats config_stats = 3; - - message AtomStats { - optional int32 tag = 1; - optional int32 count = 2; - } - - repeated AtomStats atom_stats = 7; - - message UidMapStats { - optional int32 snapshots = 1; - optional int32 changes = 2; - optional int32 bytes_used = 3; - optional int32 dropped_snapshots = 4; - optional int32 dropped_changes = 5; - } - optional UidMapStats uidmap_stats = 8; - - message AnomalyAlarmStats { - optional int32 alarms_registered = 1; - } - optional AnomalyAlarmStats anomaly_alarm_stats = 9; - - message PulledAtomStats { - optional int32 atom_id = 1; - optional int64 total_pull = 2; - optional int64 total_pull_from_cache = 3; - optional int64 min_pull_interval_sec = 4; - } - repeated PulledAtomStats pulled_atom_stats = 10; - - message LoggerErrorStats { - optional int32 logger_disconnection_sec = 1; - optional int32 error_code = 2; - } - repeated LoggerErrorStats logger_error_stats = 11; }
\ No newline at end of file diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h index c4b47dcea8a2..bbaf50a83fa9 100644 --- a/cmds/statsd/src/stats_util.h +++ b/cmds/statsd/src/stats_util.h @@ -16,7 +16,6 @@ #pragma once -#include <sstream> #include "HashableDimensionKey.h" #include "frameworks/base/cmds/statsd/src/stats_log_common.pb.h" #include "logd/LogReader.h" diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index 3238b74fb635..efed42e2613c 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -51,7 +51,8 @@ public: } MOCK_METHOD0(byteSize, size_t()); - MOCK_METHOD2(onDumpReport, void(const uint64_t timeNs, ProtoOutputStream* output)); + + MOCK_METHOD1(dropData, void(const uint64_t dropTimeNs)); }; TEST(StatsLogProcessorTest, TestRateLimitByteSize) { @@ -114,7 +115,7 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { .Times(1) .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2))); - EXPECT_CALL(mockMetricsManager, onDumpReport(_, _)).Times(1); + EXPECT_CALL(mockMetricsManager, dropData(_)).Times(1); // Expect to call the onDumpReport and skip the broadcast. p.flushIfNecessaryLocked(1, key, mockMetricsManager); diff --git a/cmds/statsd/tests/condition/StateTracker_test.cpp b/cmds/statsd/tests/condition/StateTracker_test.cpp new file mode 100644 index 000000000000..9a66254afce0 --- /dev/null +++ b/cmds/statsd/tests/condition/StateTracker_test.cpp @@ -0,0 +1,112 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/condition/StateTracker.h" +#include "tests/statsd_test_util.h" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <stdio.h> +#include <numeric> +#include <vector> + +using std::map; +using std::unordered_map; +using std::vector; + +#ifdef __ANDROID__ +namespace android { +namespace os { +namespace statsd { + +const int kUidProcTag = 27; + +SimplePredicate getUidProcStatePredicate() { + SimplePredicate simplePredicate; + simplePredicate.set_start(StringToId("UidProcState")); + + simplePredicate.mutable_dimensions()->set_field(kUidProcTag); + simplePredicate.mutable_dimensions()->add_child()->set_field(1); + simplePredicate.mutable_dimensions()->add_child()->set_field(2); + + simplePredicate.set_count_nesting(false); + return simplePredicate; +} + +void makeUidProcStateEvent(int32_t uid, int32_t state, LogEvent* event) { + event->write(uid); + event->write(state); + event->init(); +} + +TEST(StateTrackerTest, TestStateChange) { + int uid1 = 111; + int uid2 = 222; + + int state1 = 1001; + int state2 = 1002; + unordered_map<int64_t, int> trackerNameIndexMap; + trackerNameIndexMap[StringToId("UidProcState")] = 0; + vector<Matcher> primaryFields; + primaryFields.push_back(getSimpleMatcher(kUidProcTag, 1)); + StateTracker tracker(ConfigKey(12, 123), 123, 0, getUidProcStatePredicate(), + trackerNameIndexMap, primaryFields); + + LogEvent event(kUidProcTag, 0 /*timestamp*/); + makeUidProcStateEvent(uid1, state1, &event); + + vector<MatchingState> matcherState; + matcherState.push_back(MatchingState::kMatched); + vector<sp<ConditionTracker>> allPredicates; + vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); + vector<bool> changedCache(1, false); + + tracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache); + EXPECT_EQ(1ULL, tracker.mLastChangedToTrueDimensions.size()); + EXPECT_EQ(0ULL, tracker.mLastChangedToFalseDimensions.size()); + EXPECT_TRUE(changedCache[0]); + + changedCache[0] = false; + conditionCache[0] = ConditionState::kNotEvaluated; + tracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache); + EXPECT_EQ(0ULL, tracker.mLastChangedToTrueDimensions.size()); + EXPECT_EQ(0ULL, tracker.mLastChangedToFalseDimensions.size()); + EXPECT_FALSE(changedCache[0]); + + LogEvent event2(kUidProcTag, 0 /*timestamp*/); + makeUidProcStateEvent(uid1, state2, &event2); + + changedCache[0] = false; + conditionCache[0] = ConditionState::kNotEvaluated; + tracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, changedCache); + EXPECT_EQ(1ULL, tracker.mLastChangedToTrueDimensions.size()); + EXPECT_EQ(1ULL, tracker.mLastChangedToFalseDimensions.size()); + EXPECT_TRUE(changedCache[0]); + + LogEvent event3(kUidProcTag, 0 /*timestamp*/); + makeUidProcStateEvent(uid2, state1, &event3); + changedCache[0] = false; + conditionCache[0] = ConditionState::kNotEvaluated; + tracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, changedCache); + EXPECT_EQ(1ULL, tracker.mLastChangedToTrueDimensions.size()); + EXPECT_EQ(0ULL, tracker.mLastChangedToFalseDimensions.size()); + EXPECT_TRUE(changedCache[0]); +} + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp index bd114439b83c..5c4eda8348d1 100644 --- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp +++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp @@ -277,21 +277,20 @@ TEST(StatsdStatsTest, TestTimestampThreshold) { EXPECT_TRUE(stats.mConfigStats.find(key) != stats.mConfigStats.end()); const auto& configStats = stats.mConfigStats[key]; - int maxCount = StatsdStats::kMaxTimestampCount; - EXPECT_EQ(maxCount, configStats.broadcast_sent_time_sec_size()); - EXPECT_EQ(maxCount, configStats.data_drop_time_sec_size()); - EXPECT_EQ(maxCount, configStats.dump_report_time_sec_size()); + size_t maxCount = StatsdStats::kMaxTimestampCount; + EXPECT_EQ(maxCount, configStats->broadcast_sent_time_sec.size()); + EXPECT_EQ(maxCount, configStats->data_drop_time_sec.size()); + EXPECT_EQ(maxCount, configStats->dump_report_time_sec.size()); // the oldest timestamp is the second timestamp in history - EXPECT_EQ(1, configStats.broadcast_sent_time_sec(0)); - EXPECT_EQ(1, configStats.broadcast_sent_time_sec(0)); - EXPECT_EQ(1, configStats.broadcast_sent_time_sec(0)); + EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front()); + EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front()); + EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front()); // the last timestamp is the newest timestamp. - EXPECT_EQ(newTimestamp, - configStats.broadcast_sent_time_sec(StatsdStats::kMaxTimestampCount - 1)); - EXPECT_EQ(newTimestamp, configStats.data_drop_time_sec(StatsdStats::kMaxTimestampCount - 1)); - EXPECT_EQ(newTimestamp, configStats.dump_report_time_sec(StatsdStats::kMaxTimestampCount - 1)); + EXPECT_EQ(newTimestamp, configStats->broadcast_sent_time_sec.back()); + EXPECT_EQ(newTimestamp, configStats->data_drop_time_sec.back()); + EXPECT_EQ(newTimestamp, configStats->dump_report_time_sec.back()); } } // namespace statsd diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java index a12def0db9fb..bed4d9809692 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java @@ -50,7 +50,7 @@ import android.widget.Toast; import com.android.os.StatsLog.ConfigMetricsReport; import com.android.os.StatsLog.ConfigMetricsReportList; -import com.android.os.StatsLogCommon.StatsdStatsReport; +import com.android.os.StatsLog.StatsdStatsReport; import com.android.internal.os.StatsdConfigProto.TimeUnit; import java.util.ArrayList; diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java index 58cbcd89581e..3939e7e0b2fa 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java @@ -16,7 +16,7 @@ package com.android.statsd.loadtest; import android.content.Context; -import com.android.os.StatsLogCommon.StatsdStatsReport; +import com.android.os.StatsLog.StatsdStatsReport; import com.android.internal.os.StatsdConfigProto.TimeUnit; public class StatsdStatsRecorder extends PerfDataRecorder { diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 2c400e00d9a2..fb4d3b8f7323 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -526,41 +526,22 @@ Landroid/content/res/AssetFileDescriptor;->mLength:J Landroid/content/res/AssetFileDescriptor;->mStartOffset:J Landroid/content/res/AssetManager;->addAssetPathAsSharedLibrary(Ljava/lang/String;)I Landroid/content/res/AssetManager;->addAssetPath(Ljava/lang/String;)I -Landroid/content/res/AssetManager;->addAssetPathNative(Ljava/lang/String;Z)I -Landroid/content/res/AssetManager;->addAssetPaths([Ljava/lang/String;)[I -Landroid/content/res/AssetManager;->applyStyle(JIIJ[IIJJ)V -Landroid/content/res/AssetManager;->ensureStringBlocks()[Landroid/content/res/StringBlock; -Landroid/content/res/AssetManager;->getArraySize(I)I Landroid/content/res/AssetManager;->getAssignedPackageIdentifiers()Landroid/util/SparseArray; -Landroid/content/res/AssetManager;->getCookieName(I)Ljava/lang/String; -Landroid/content/res/AssetManager;->getNativeStringBlock(I)J Landroid/content/res/AssetManager;->getResourceBagText(II)Ljava/lang/CharSequence; Landroid/content/res/AssetManager;->getResourceEntryName(I)Ljava/lang/String; Landroid/content/res/AssetManager;->getResourceIdentifier(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I Landroid/content/res/AssetManager;->getResourceName(I)Ljava/lang/String; Landroid/content/res/AssetManager;->getResourcePackageName(I)Ljava/lang/String; Landroid/content/res/AssetManager;->getResourceTypeName(I)Ljava/lang/String; -Landroid/content/res/AssetManager;->getStringBlockCount()I Landroid/content/res/AssetManager;-><init>()V Landroid/content/res/AssetManager;->isUpToDate()Z -Landroid/content/res/AssetManager;->loadResourceBagValue(IILandroid/util/TypedValue;Z)I -Landroid/content/res/AssetManager;->loadResourceValue(ISLandroid/util/TypedValue;Z)I -Landroid/content/res/AssetManager;->loadThemeAttributeValue(JILandroid/util/TypedValue;Z)I Landroid/content/res/AssetManager;->mObject:J -Landroid/content/res/AssetManager;->mStringBlocks:[Landroid/content/res/StringBlock; -Landroid/content/res/AssetManager;->openNonAssetFdNative(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor; Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;I)Ljava/io/InputStream; Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;)Ljava/io/InputStream; Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;I)Ljava/io/InputStream; Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;)Ljava/io/InputStream; -Landroid/content/res/AssetManager;->openNonAssetNative(ILjava/lang/String;I)J -Landroid/content/res/AssetManager;->openXmlAssetNative(ILjava/lang/String;)J Landroid/content/res/AssetManager;->resolveAttrs(JII[I[I[I[I)Z -Landroid/content/res/AssetManager;->retrieveArray(I[I)I -Landroid/content/res/AssetManager;->retrieveAttributes(J[I[I[I)Z Landroid/content/res/AssetManager;->setConfiguration(IILjava/lang/String;IIIIIIIIIIIIIII)V -Landroid/content/res/AssetManager;->STYLE_NUM_ENTRIES:I -Landroid/content/res/AssetManager;->STYLE_RESOURCE_ID:I Landroid/content/res/ColorStateList$ColorStateListFactory;-><init>(Landroid/content/res/ColorStateList;)V Landroid/content/res/ColorStateList;->mColors:[I Landroid/content/res/ColorStateList;->mDefaultColor:I @@ -602,7 +583,6 @@ Landroid/content/res/TypedArray;->mResources:Landroid/content/res/Resources; Landroid/content/res/TypedArray;->mTheme:Landroid/content/res/Resources$Theme; Landroid/content/res/TypedArray;->mValue:Landroid/util/TypedValue; Landroid/content/res/TypedArray;->mXml:Landroid/content/res/XmlBlock$Parser; -Landroid/content/res/XmlBlock;->close()V Landroid/content/res/XmlBlock;-><init>([B)V Landroid/content/res/XmlBlock;->newParser()Landroid/content/res/XmlResourceParser; Landroid/content/res/XmlBlock$Parser;->mParseState:J diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 3d04e2c64bee..99c5d2b9a927 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -6366,6 +6366,8 @@ public class Activity extends ContextThemeWrapper } else { writer.print(prefix); writer.println("No AutofillManager"); } + + ResourcesManager.getInstance().dump(prefix, writer); } /** diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index d378f227d921..ddd065692ea2 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -45,6 +45,8 @@ interface INotificationManager void clearData(String pkg, int uid, boolean fromApp); void enqueueToast(String pkg, ITransientNotification callback, int duration); void cancelToast(String pkg, ITransientNotification callback); + void finishToken(String pkg, ITransientNotification callback); + void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id, in Notification notification, int userId); void cancelNotificationWithTag(String pkg, String tag, int id, int userId); diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java index 2ba4c00c3f46..58d0aaf5b621 100644 --- a/core/java/android/app/InstantAppResolverService.java +++ b/core/java/android/app/InstantAppResolverService.java @@ -62,7 +62,7 @@ public abstract class InstantAppResolverService extends Service { @Deprecated public void onGetInstantAppResolveInfo( int digestPrefix[], String token, InstantAppResolutionCallback callback) { - throw new IllegalStateException("Must define"); + throw new IllegalStateException("Must define onGetInstantAppResolveInfo"); } /** @@ -80,10 +80,26 @@ public abstract class InstantAppResolverService extends Service { } /** - * Called to retrieve resolve info for instant applications immediately. + * Called to retrieve resolve info for instant applications immediately. The response will be + * ignored if not provided within a reasonable time. {@link InstantAppResolveInfo}s provided + * in response to this method may be partial to request a second phase of resolution which will + * result in a subsequent call to + * {@link #onGetInstantAppIntentFilter(Intent, int[], String, InstantAppResolutionCallback)} * - * @param sanitizedIntent The sanitized {@link Intent} used for resolution. + * + * @param sanitizedIntent The sanitized {@link Intent} used for resolution. A sanitized Intent + * is an intent with potential PII removed from the original intent. + * Fields removed include extras and the host + path of the data, if + * defined. * @param hostDigestPrefix The hash prefix of the instant app's domain. + * @param token A unique identifier that will be provided in calls to + * {@link #onGetInstantAppIntentFilter(Intent, int[], String, + * InstantAppResolutionCallback)} + * and provided to the installer via {@link Intent#EXTRA_INSTANT_APP_TOKEN} to + * tie a single launch together. + * @param callback The {@link InstantAppResolutionCallback} to provide results to. + * + * @see InstantAppResolveInfo */ public void onGetInstantAppResolveInfo(Intent sanitizedIntent, int[] hostDigestPrefix, String token, InstantAppResolutionCallback callback) { @@ -96,12 +112,20 @@ public abstract class InstantAppResolverService extends Service { } /** - * Called to retrieve intent filters for instant applications from potentially expensive - * sources. + * Called to retrieve intent filters for potentially matching instant applications. Unlike + * {@link #onGetInstantAppResolveInfo(Intent, int[], String, InstantAppResolutionCallback)}, + * the response may take as long as necessary to respond. All {@link InstantAppResolveInfo}s + * provided in response to this method must be completely populated. * * @param sanitizedIntent The sanitized {@link Intent} used for resolution. * @param hostDigestPrefix The hash prefix of the instant app's domain or null if no host is * defined. + * @param token A unique identifier that was provided in + * {@link #onGetInstantAppResolveInfo(Intent, int[], String, + * InstantAppResolutionCallback)} + * and provided to the currently visible installer via + * {@link Intent#EXTRA_INSTANT_APP_TOKEN}. + * @param callback The {@link InstantAppResolutionCallback} to provide results to */ public void onGetInstantAppIntentFilter(Intent sanitizedIntent, int[] hostDigestPrefix, String token, InstantAppResolutionCallback callback) { diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index fb11272d7e62..fc5ea6607d87 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -21,6 +21,7 @@ import static android.app.ActivityThread.DEBUG_CONFIGURATION; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.ActivityInfo; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.CompatResources; import android.content.res.CompatibilityInfo; @@ -34,6 +35,7 @@ import android.os.Trace; import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.Log; +import android.util.LruCache; import android.util.Pair; import android.util.Slog; import android.view.Display; @@ -41,9 +43,13 @@ import android.view.DisplayAdjustments; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.IndentingPrintWriter; +import java.io.IOException; +import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collection; import java.util.Objects; import java.util.WeakHashMap; import java.util.function.Predicate; @@ -59,12 +65,7 @@ public class ResourcesManager { * Predicate that returns true if a WeakReference is gc'ed. */ private static final Predicate<WeakReference<Resources>> sEmptyReferencePredicate = - new Predicate<WeakReference<Resources>>() { - @Override - public boolean test(WeakReference<Resources> weakRef) { - return weakRef == null || weakRef.get() == null; - } - }; + weakRef -> weakRef == null || weakRef.get() == null; /** * The global compatibility settings. @@ -89,6 +90,48 @@ public class ResourcesManager { */ private final ArrayList<WeakReference<Resources>> mResourceReferences = new ArrayList<>(); + private static class ApkKey { + public final String path; + public final boolean sharedLib; + public final boolean overlay; + + ApkKey(String path, boolean sharedLib, boolean overlay) { + this.path = path; + this.sharedLib = sharedLib; + this.overlay = overlay; + } + + @Override + public int hashCode() { + int result = 1; + result = 31 * result + this.path.hashCode(); + result = 31 * result + Boolean.hashCode(this.sharedLib); + result = 31 * result + Boolean.hashCode(this.overlay); + return result; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ApkKey)) { + return false; + } + ApkKey other = (ApkKey) obj; + return this.path.equals(other.path) && this.sharedLib == other.sharedLib + && this.overlay == other.overlay; + } + } + + /** + * The ApkAssets we are caching and intend to hold strong references to. + */ + private final LruCache<ApkKey, ApkAssets> mLoadedApkAssets = new LruCache<>(15); + + /** + * The ApkAssets that are being referenced in the wild that we can reuse, even if they aren't + * in our LRU cache. Bonus resources :) + */ + private final ArrayMap<ApkKey, WeakReference<ApkAssets>> mCachedApkAssets = new ArrayMap<>(); + /** * Resources and base configuration override associated with an Activity. */ @@ -260,6 +303,43 @@ public class ResourcesManager { } } + private static String overlayPathToIdmapPath(String path) { + return "/data/resource-cache/" + path.substring(1).replace('/', '@') + "@idmap"; + } + + private @NonNull ApkAssets loadApkAssets(String path, boolean sharedLib, boolean overlay) + throws IOException { + final ApkKey newKey = new ApkKey(path, sharedLib, overlay); + ApkAssets apkAssets = mLoadedApkAssets.get(newKey); + if (apkAssets != null) { + return apkAssets; + } + + // Optimistically check if this ApkAssets exists somewhere else. + final WeakReference<ApkAssets> apkAssetsRef = mCachedApkAssets.get(newKey); + if (apkAssetsRef != null) { + apkAssets = apkAssetsRef.get(); + if (apkAssets != null) { + mLoadedApkAssets.put(newKey, apkAssets); + return apkAssets; + } else { + // Clean up the reference. + mCachedApkAssets.remove(newKey); + } + } + + // We must load this from disk. + if (overlay) { + apkAssets = ApkAssets.loadOverlayFromPath(overlayPathToIdmapPath(path), + false /*system*/); + } else { + apkAssets = ApkAssets.loadFromPath(path, false /*system*/, sharedLib); + } + mLoadedApkAssets.put(newKey, apkAssets); + mCachedApkAssets.put(newKey, new WeakReference<>(apkAssets)); + return apkAssets; + } + /** * Creates an AssetManager from the paths within the ResourcesKey. * @@ -270,13 +350,16 @@ public class ResourcesManager { */ @VisibleForTesting protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) { - AssetManager assets = new AssetManager(); + final AssetManager.Builder builder = new AssetManager.Builder(); // resDir can be null if the 'android' package is creating a new Resources object. // This is fine, since each AssetManager automatically loads the 'android' package // already. if (key.mResDir != null) { - if (assets.addAssetPath(key.mResDir) == 0) { + try { + builder.addApkAssets(loadApkAssets(key.mResDir, false /*sharedLib*/, + false /*overlay*/)); + } catch (IOException e) { Log.e(TAG, "failed to add asset path " + key.mResDir); return null; } @@ -284,7 +367,10 @@ public class ResourcesManager { if (key.mSplitResDirs != null) { for (final String splitResDir : key.mSplitResDirs) { - if (assets.addAssetPath(splitResDir) == 0) { + try { + builder.addApkAssets(loadApkAssets(splitResDir, false /*sharedLib*/, + false /*overlay*/)); + } catch (IOException e) { Log.e(TAG, "failed to add split asset path " + splitResDir); return null; } @@ -293,7 +379,14 @@ public class ResourcesManager { if (key.mOverlayDirs != null) { for (final String idmapPath : key.mOverlayDirs) { - assets.addOverlayPath(idmapPath); + try { + builder.addApkAssets(loadApkAssets(idmapPath, false /*sharedLib*/, + true /*overlay*/)); + } catch (IOException e) { + Log.w(TAG, "failed to add overlay path " + idmapPath); + + // continue. + } } } @@ -302,14 +395,73 @@ public class ResourcesManager { if (libDir.endsWith(".apk")) { // Avoid opening files we know do not have resources, // like code-only .jar files. - if (assets.addAssetPathAsSharedLibrary(libDir) == 0) { + try { + builder.addApkAssets(loadApkAssets(libDir, true /*sharedLib*/, + false /*overlay*/)); + } catch (IOException e) { Log.w(TAG, "Asset path '" + libDir + "' does not exist or contains no resources."); + + // continue. } } } } - return assets; + + return builder.build(); + } + + private static <T> int countLiveReferences(Collection<WeakReference<T>> collection) { + int count = 0; + for (WeakReference<T> ref : collection) { + final T value = ref != null ? ref.get() : null; + if (value != null) { + count++; + } + } + return count; + } + + /** + * @hide + */ + public void dump(String prefix, PrintWriter printWriter) { + synchronized (this) { + IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); + for (int i = 0; i < prefix.length() / 2; i++) { + pw.increaseIndent(); + } + + pw.println("ResourcesManager:"); + pw.increaseIndent(); + pw.print("cached apks: total="); + pw.print(mLoadedApkAssets.size()); + pw.print(" created="); + pw.print(mLoadedApkAssets.createCount()); + pw.print(" evicted="); + pw.print(mLoadedApkAssets.evictionCount()); + pw.print(" hit="); + pw.print(mLoadedApkAssets.hitCount()); + pw.print(" miss="); + pw.print(mLoadedApkAssets.missCount()); + pw.print(" max="); + pw.print(mLoadedApkAssets.maxSize()); + pw.println(); + + pw.print("total apks: "); + pw.println(countLiveReferences(mCachedApkAssets.values())); + + pw.print("resources: "); + + int references = countLiveReferences(mResourceReferences); + for (ActivityResources activityResources : mActivityResourceReferences.values()) { + references += countLiveReferences(activityResources.activityResources); + } + pw.println(references); + + pw.print("resource impls: "); + pw.println(countLiveReferences(mResourceImpls.values())); + } } private Configuration generateConfig(@NonNull ResourcesKey key, @NonNull DisplayMetrics dm) { @@ -630,28 +782,16 @@ public class ResourcesManager { // We will create the ResourcesImpl object outside of holding this lock. } - } - - // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now. - ResourcesImpl resourcesImpl = createResourcesImpl(key); - if (resourcesImpl == null) { - return null; - } - synchronized (this) { - ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key); - if (existingResourcesImpl != null) { - if (DEBUG) { - Slog.d(TAG, "- got beat! existing impl=" + existingResourcesImpl - + " new impl=" + resourcesImpl); - } - resourcesImpl.getAssets().close(); - resourcesImpl = existingResourcesImpl; - } else { - // Add this ResourcesImpl to the cache. - mResourceImpls.put(key, new WeakReference<>(resourcesImpl)); + // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now. + ResourcesImpl resourcesImpl = createResourcesImpl(key); + if (resourcesImpl == null) { + return null; } + // Add this ResourcesImpl to the cache. + mResourceImpls.put(key, new WeakReference<>(resourcesImpl)); + final Resources resources; if (activityToken != null) { resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader, diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 2c4bf8260fe6..ee5ea806da34 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -3712,7 +3712,9 @@ public class DevicePolicyManager { public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0; /** - * Disable all keyguard widgets. Has no effect. + * Disable all keyguard widgets. Has no effect starting from + * {@link android.os.Build.VERSION_CODES#LOLLIPOP} since keyguard widget is only supported + * on Android versions lower than 5.0. */ public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1 << 0; diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java index b5c69d8897c7..65e54f97e215 100644 --- a/core/java/android/app/slice/Slice.java +++ b/core/java/android/app/slice/Slice.java @@ -65,7 +65,8 @@ public final class Slice implements Parcelable { HINT_TOGGLE, HINT_HORIZONTAL, HINT_PARTIAL, - HINT_SEE_MORE + HINT_SEE_MORE, + HINT_KEY_WORDS }) @Retention(RetentionPolicy.SOURCE) public @interface SliceHint {} @@ -149,6 +150,11 @@ public final class Slice implements Parcelable { */ public static final String HINT_CALLER_NEEDED = "caller_needed"; /** + * A hint to indicate that the contents of this subslice represent a list of keywords + * related to the parent slice. + */ + public static final String HINT_KEY_WORDS = "key_words"; + /** * Key to retrieve an extra added to an intent when a control is changed. */ public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE"; diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 1a5de5690cae..cb353576bd8d 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -84,7 +84,7 @@ public final class CompanionDeviceManager { public abstract void onDeviceFound(IntentSender chooserLauncher); /** - * Called if there was an error looking for device(s), e.g. timeout + * Called if there was an error looking for device(s) * * @param error the cause of the error */ diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java new file mode 100644 index 000000000000..9f5c877e7081 --- /dev/null +++ b/core/java/android/content/PermissionChecker.java @@ -0,0 +1,173 @@ +/* + * 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.content; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.AppOpsManager; +import android.content.pm.PackageManager; +import android.os.Binder; +import android.os.Process; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * This class provides permission check APIs that verify both the + * permission and the associated app op for this permission if + * such is defined. + * <p> + * In the new permission model permissions with protection level + * dangerous are runtime permissions. For apps targeting {@link android.os.Build.VERSION_CODES#M} + * and above the user may not grant such permissions or revoke + * them at any time. For apps targeting API lower than {@link android.os.Build.VERSION_CODES#M} + * these permissions are always granted as such apps do not expect + * permission revocations and would crash. Therefore, when the + * user disables a permission for a legacy app in the UI the + * platform disables the APIs guarded by this permission making + * them a no-op which is doing nothing or returning an empty + * result or default error. + * </p> + * <p> + * It is important that when you perform an operation on behalf of + * another app you use these APIs to check for permissions as the + * app may be a legacy app that does not participate in the new + * permission model for which the user had disabled the "permission" + * which is achieved by disallowing the corresponding app op. + * </p> + * + * @hide + */ +public final class PermissionChecker { + /** Permission result: The permission is granted. */ + public static final int PERMISSION_GRANTED = PackageManager.PERMISSION_GRANTED; + + /** Permission result: The permission is denied. */ + public static final int PERMISSION_DENIED = PackageManager.PERMISSION_DENIED; + + /** Permission result: The permission is denied because the app op is not allowed. */ + public static final int PERMISSION_DENIED_APP_OP = PackageManager.PERMISSION_DENIED - 1; + + /** @hide */ + @IntDef({PERMISSION_GRANTED, + PERMISSION_DENIED, + PERMISSION_DENIED_APP_OP}) + @Retention(RetentionPolicy.SOURCE) + public @interface PermissionResult {} + + private PermissionChecker() { + /* do nothing */ + } + + /** + * Checks whether a given package in a UID and PID has a given permission + * and whether the app op that corresponds to this permission is allowed. + * + * @param context Context for accessing resources. + * @param permission The permission to check. + * @param pid The process id for which to check. + * @param uid The uid for which to check. + * @param packageName The package name for which to check. If null the + * the first package for the calling UID will be used. + * @return The permission check result which is either {@link #PERMISSION_GRANTED} + * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. + */ + @PermissionResult + public static int checkPermission(@NonNull Context context, @NonNull String permission, + int pid, int uid, @Nullable String packageName) { + if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) { + return PERMISSION_DENIED; + } + + AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); + String op = appOpsManager.permissionToOp(permission); + if (op == null) { + return PERMISSION_GRANTED; + } + + if (packageName == null) { + String[] packageNames = context.getPackageManager().getPackagesForUid(uid); + if (packageNames == null || packageNames.length <= 0) { + return PERMISSION_DENIED; + } + packageName = packageNames[0]; + } + + if (appOpsManager.noteProxyOpNoThrow(op, packageName) + != AppOpsManager.MODE_ALLOWED) { + return PERMISSION_DENIED_APP_OP; + } + + return PERMISSION_GRANTED; + } + + /** + * Checks whether your app has a given permission and whether the app op + * that corresponds to this permission is allowed. + * + * @param context Context for accessing resources. + * @param permission The permission to check. + * @return The permission check result which is either {@link #PERMISSION_GRANTED} + * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. + */ + @PermissionResult + public static int checkSelfPermission(@NonNull Context context, + @NonNull String permission) { + return checkPermission(context, permission, Process.myPid(), + Process.myUid(), context.getPackageName()); + } + + /** + * Checks whether the IPC you are handling has a given permission and whether + * the app op that corresponds to this permission is allowed. + * + * @param context Context for accessing resources. + * @param permission The permission to check. + * @param packageName The package name making the IPC. If null the + * the first package for the calling UID will be used. + * @return The permission check result which is either {@link #PERMISSION_GRANTED} + * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. + */ + @PermissionResult + public static int checkCallingPermission(@NonNull Context context, + @NonNull String permission, @Nullable String packageName) { + if (Binder.getCallingPid() == Process.myPid()) { + return PERMISSION_DENIED; + } + return checkPermission(context, permission, Binder.getCallingPid(), + Binder.getCallingUid(), packageName); + } + + /** + * Checks whether the IPC you are handling or your app has a given permission + * and whether the app op that corresponds to this permission is allowed. + * + * @param context Context for accessing resources. + * @param permission The permission to check. + * @return The permission check result which is either {@link #PERMISSION_GRANTED} + * or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}. + */ + @PermissionResult + public static int checkCallingOrSelfPermission(@NonNull Context context, + @NonNull String permission) { + String packageName = (Binder.getCallingPid() == Process.myPid()) + ? context.getPackageName() : null; + return checkPermission(context, permission, Binder.getCallingPid(), + Binder.getCallingUid(), packageName); + } +} diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index b2c9edd4758f..f7649c9edf01 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -763,15 +763,13 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public String[] resourceDirs; /** - * String retrieved from the seinfo tag found in selinux policy. This value - * can be overridden with a value set through the mac_permissions.xml policy - * construct. This value is useful in setting an SELinux security context on - * the process as well as its data directory. The String default is being used - * here to represent a catchall label when no policy matches. + * String retrieved from the seinfo tag found in selinux policy. This value can be set through + * the mac_permissions.xml policy construct. This value is used for setting an SELinux security + * context on the process as well as its data directory. * * {@hide} */ - public String seInfo = "default"; + public String seInfo; /** * The seinfo tag generated per-user. This value may change based upon the diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java index 3a95a5f87d97..8184361bca80 100644 --- a/core/java/android/content/pm/InstantAppResolveInfo.java +++ b/core/java/android/content/pm/InstantAppResolveInfo.java @@ -78,8 +78,7 @@ public final class InstantAppResolveInfo implements Parcelable { private final Bundle mExtras; /** * A flag that indicates that the resolver is aware that an app may match, but would prefer - * that the installer get the sanitized intent to decide. This should not be used for - * resolutions that include a host and will be ignored in such cases. + * that the installer get the sanitized intent to decide. */ private final boolean mShouldLetInstallerDecide; @@ -96,7 +95,21 @@ public final class InstantAppResolveInfo implements Parcelable { this(digest, packageName, filters, versionCode, extras, false); } - /** Constructor for intent-based InstantApp resolution results with extras. */ + /** Constructor for intent-based InstantApp resolution results by hostname. */ + public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName, + @Nullable List<InstantAppIntentFilter> filters) { + this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/, + null /* extras */); + } + + /** + * Constructor that indicates that resolution could be delegated to the installer when the + * sanitized intent contains enough information to resolve completely. + */ + public InstantAppResolveInfo(@Nullable Bundle extras) { + this(InstantAppDigest.UNDEFINED, null, null, -1, extras, true); + } + private InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName, @Nullable List<InstantAppIntentFilter> filters, long versionCode, @Nullable Bundle extras, boolean shouldLetInstallerDecide) { @@ -118,21 +131,6 @@ public final class InstantAppResolveInfo implements Parcelable { mShouldLetInstallerDecide = shouldLetInstallerDecide; } - /** Constructor for intent-based InstantApp resolution results by hostname. */ - public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName, - @Nullable List<InstantAppIntentFilter> filters) { - this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/, - null /* extras */); - } - - /** - * Constructor that creates a "let the installer decide" response with optional included - * extras. - */ - public InstantAppResolveInfo(@Nullable Bundle extras) { - this(InstantAppDigest.UNDEFINED, null, null, -1, extras, true); - } - InstantAppResolveInfo(Parcel in) { mShouldLetInstallerDecide = in.readBoolean(); mExtras = in.readBundle(); @@ -150,7 +148,11 @@ public final class InstantAppResolveInfo implements Parcelable { } } - /** Returns true if the installer should be notified that it should query for packages. */ + /** + * Returns true if the resolver is aware that an app may match, but would prefer + * that the installer get the sanitized intent to decide. This should not be true for + * resolutions that include a host and will be ignored in such cases. + */ public boolean shouldLetInstallerDecide() { return mShouldLetInstallerDecide; } @@ -231,6 +233,11 @@ public final class InstantAppResolveInfo implements Parcelable { @SystemApi public static final class InstantAppDigest implements Parcelable { static final int DIGEST_MASK = 0xfffff000; + + /** + * A special instance that represents and undefined digest used for cases that a host was + * not provided or is irrelevant to the response. + */ public static final InstantAppDigest UNDEFINED = new InstantAppDigest(new byte[][]{}, new int[]{}); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 04a028b23806..5d5a9782884a 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -54,6 +54,7 @@ import android.content.pm.PackageParserCacheHelper.WriteHelper; import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.SplitAssetDependencyLoader; import android.content.pm.split.SplitAssetLoader; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -1287,7 +1288,6 @@ public class PackageParser { */ @Deprecated public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException { - final AssetManager assets = newConfiguredAssetManager(); final PackageLite lite = parseMonolithicPackageLite(apkFile, flags); if (mOnlyCoreApps) { if (!lite.coreApp) { @@ -1296,8 +1296,9 @@ public class PackageParser { } } + final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags); try { - final Package pkg = parseBaseApk(apkFile, assets, flags); + final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags); pkg.setCodePath(apkFile.getCanonicalPath()); pkg.setUse32bitAbi(lite.use32bitAbi); return pkg; @@ -1305,26 +1306,8 @@ public class PackageParser { throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, "Failed to get path: " + apkFile, e); } finally { - IoUtils.closeQuietly(assets); - } - } - - private static int loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags) - throws PackageParserException { - if ((flags & PARSE_MUST_BE_APK) != 0 && !isApkPath(apkPath)) { - throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, - "Invalid package file: " + apkPath); - } - - // The AssetManager guarantees uniqueness for asset paths, so if this asset path - // already exists in the AssetManager, addAssetPath will only return the cookie - // assigned to it. - int cookie = assets.addAssetPath(apkPath); - if (cookie == 0) { - throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, - "Failed adding asset path: " + apkPath); + IoUtils.closeQuietly(assetLoader); } - return cookie; } private Package parseBaseApk(File apkFile, AssetManager assets, int flags) @@ -1342,13 +1325,15 @@ public class PackageParser { if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath); - final int cookie = loadApkIntoAssetManager(assets, apkPath, flags); - - Resources res = null; XmlResourceParser parser = null; try { - res = new Resources(assets, mMetrics, null); + final int cookie = assets.findCookieForPath(apkPath); + if (cookie == 0) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Failed adding asset path: " + apkPath); + } parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); + final Resources res = new Resources(assets, mMetrics, null); final String[] outError = new String[1]; final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError); @@ -1383,15 +1368,18 @@ public class PackageParser { if (DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath); - final int cookie = loadApkIntoAssetManager(assets, apkPath, flags); - final Resources res; XmlResourceParser parser = null; try { - res = new Resources(assets, mMetrics, null); - assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - Build.VERSION.RESOURCES_SDK_INT); + // This must always succeed, as the path has been added to the AssetManager before. + final int cookie = assets.findCookieForPath(apkPath); + if (cookie == 0) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Failed adding asset path: " + apkPath); + } + parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); + res = new Resources(assets, mMetrics, null); final String[] outError = new String[1]; pkg = parseSplitApk(pkg, res, parser, flags, splitIndex, outError); @@ -1593,21 +1581,19 @@ public class PackageParser { int flags) throws PackageParserException { final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath(); - AssetManager assets = null; XmlResourceParser parser = null; try { - assets = newConfiguredAssetManager(); - int cookie = fd != null - ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath); - if (cookie == 0) { + final ApkAssets apkAssets; + try { + apkAssets = fd != null + ? ApkAssets.loadFromFd(fd, debugPathName, false, false) + : ApkAssets.loadFromPath(apkPath); + } catch (IOException e) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse " + apkPath); } - final DisplayMetrics metrics = new DisplayMetrics(); - metrics.setToDefaults(); - - parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); + parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME); final SigningDetails signingDetails; if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) { @@ -1634,7 +1620,7 @@ public class PackageParser { "Failed to parse " + apkPath, e); } finally { IoUtils.closeQuietly(parser); - IoUtils.closeQuietly(assets); + // TODO(b/72056911): Implement and call close() on ApkAssets. } } diff --git a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java index 99eb4702d32e..9e3a8f48996c 100644 --- a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java +++ b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java @@ -15,10 +15,13 @@ */ package android.content.pm.split; -import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; +import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK; import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageParserException; +import android.content.pm.PackageParser.ParseFlags; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.os.Build; @@ -26,6 +29,8 @@ import com.android.internal.util.ArrayUtils; import libcore.io.IoUtils; +import java.io.IOException; + /** * Loads the base and split APKs into a single AssetManager. * @hide @@ -33,68 +38,66 @@ import libcore.io.IoUtils; public class DefaultSplitAssetLoader implements SplitAssetLoader { private final String mBaseCodePath; private final String[] mSplitCodePaths; - private final int mFlags; - + private final @ParseFlags int mFlags; private AssetManager mCachedAssetManager; - public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, int flags) { + public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, @ParseFlags int flags) { mBaseCodePath = pkg.baseCodePath; mSplitCodePaths = pkg.splitCodePaths; mFlags = flags; } - private static void loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags) - throws PackageParser.PackageParserException { - if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(apkPath)) { - throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, - "Invalid package file: " + apkPath); + private static ApkAssets loadApkAssets(String path, @ParseFlags int flags) + throws PackageParserException { + if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) { + throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, + "Invalid package file: " + path); } - if (assets.addAssetPath(apkPath) == 0) { - throw new PackageParser.PackageParserException( - INSTALL_PARSE_FAILED_BAD_MANIFEST, - "Failed adding asset path: " + apkPath); + try { + return ApkAssets.loadFromPath(path); + } catch (IOException e) { + throw new PackageParserException(INSTALL_FAILED_INVALID_APK, + "Failed to load APK at path " + path, e); } } @Override - public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException { + public AssetManager getBaseAssetManager() throws PackageParserException { if (mCachedAssetManager != null) { return mCachedAssetManager; } - AssetManager assets = new AssetManager(); - try { - assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - Build.VERSION.RESOURCES_SDK_INT); - loadApkIntoAssetManager(assets, mBaseCodePath, mFlags); - - if (!ArrayUtils.isEmpty(mSplitCodePaths)) { - for (String apkPath : mSplitCodePaths) { - loadApkIntoAssetManager(assets, apkPath, mFlags); - } - } + ApkAssets[] apkAssets = new ApkAssets[(mSplitCodePaths != null + ? mSplitCodePaths.length : 0) + 1]; - mCachedAssetManager = assets; - assets = null; - return mCachedAssetManager; - } finally { - if (assets != null) { - IoUtils.closeQuietly(assets); + // Load the base. + int splitIdx = 0; + apkAssets[splitIdx++] = loadApkAssets(mBaseCodePath, mFlags); + + // Load any splits. + if (!ArrayUtils.isEmpty(mSplitCodePaths)) { + for (String apkPath : mSplitCodePaths) { + apkAssets[splitIdx++] = loadApkAssets(apkPath, mFlags); } } + + AssetManager assets = new AssetManager(); + assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Build.VERSION.RESOURCES_SDK_INT); + assets.setApkAssets(apkAssets, false /*invalidateCaches*/); + + mCachedAssetManager = assets; + return mCachedAssetManager; } @Override - public AssetManager getSplitAssetManager(int splitIdx) - throws PackageParser.PackageParserException { + public AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException { return getBaseAssetManager(); } @Override public void close() throws Exception { - if (mCachedAssetManager != null) { - IoUtils.closeQuietly(mCachedAssetManager); - } + IoUtils.closeQuietly(mCachedAssetManager); } } diff --git a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java index 16023f0d9d97..58eaabfa62f2 100644 --- a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java +++ b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java @@ -15,17 +15,21 @@ */ package android.content.pm.split; -import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK; import android.annotation.NonNull; +import android.content.pm.PackageManager; import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageParserException; +import android.content.pm.PackageParser.ParseFlags; +import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.os.Build; import android.util.SparseArray; import libcore.io.IoUtils; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -34,17 +38,15 @@ import java.util.Collections; * is to be used when an application opts-in to isolated split loading. * @hide */ -public class SplitAssetDependencyLoader - extends SplitDependencyLoader<PackageParser.PackageParserException> +public class SplitAssetDependencyLoader extends SplitDependencyLoader<PackageParserException> implements SplitAssetLoader { private final String[] mSplitPaths; - private final int mFlags; - - private String[][] mCachedPaths; - private AssetManager[] mCachedAssetManagers; + private final @ParseFlags int mFlags; + private final ApkAssets[][] mCachedSplitApks; + private final AssetManager[] mCachedAssetManagers; public SplitAssetDependencyLoader(PackageParser.PackageLite pkg, - SparseArray<int[]> dependencies, int flags) { + SparseArray<int[]> dependencies, @ParseFlags int flags) { super(dependencies); // The base is inserted into index 0, so we need to shift all the splits by 1. @@ -53,7 +55,7 @@ public class SplitAssetDependencyLoader System.arraycopy(pkg.splitCodePaths, 0, mSplitPaths, 1, pkg.splitCodePaths.length); mFlags = flags; - mCachedPaths = new String[mSplitPaths.length][]; + mCachedSplitApks = new ApkAssets[mSplitPaths.length][]; mCachedAssetManagers = new AssetManager[mSplitPaths.length]; } @@ -62,58 +64,60 @@ public class SplitAssetDependencyLoader return mCachedAssetManagers[splitIdx] != null; } - private static AssetManager createAssetManagerWithPaths(String[] assetPaths, int flags) - throws PackageParser.PackageParserException { - final AssetManager assets = new AssetManager(); + private static ApkAssets loadApkAssets(String path, @ParseFlags int flags) + throws PackageParserException { + if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) { + throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, + "Invalid package file: " + path); + } + try { - assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - Build.VERSION.RESOURCES_SDK_INT); - - for (String assetPath : assetPaths) { - if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && - !PackageParser.isApkPath(assetPath)) { - throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, - "Invalid package file: " + assetPath); - } - - if (assets.addAssetPath(assetPath) == 0) { - throw new PackageParser.PackageParserException( - INSTALL_PARSE_FAILED_BAD_MANIFEST, - "Failed adding asset path: " + assetPath); - } - } - return assets; - } catch (Throwable e) { - IoUtils.closeQuietly(assets); - throw e; + return ApkAssets.loadFromPath(path); + } catch (IOException e) { + throw new PackageParserException(PackageManager.INSTALL_FAILED_INVALID_APK, + "Failed to load APK at path " + path, e); } } + private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) { + final AssetManager assets = new AssetManager(); + assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Build.VERSION.RESOURCES_SDK_INT); + assets.setApkAssets(apkAssets, false /*invalidateCaches*/); + return assets; + } + @Override protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices, - int parentSplitIdx) throws PackageParser.PackageParserException { - final ArrayList<String> assetPaths = new ArrayList<>(); + int parentSplitIdx) throws PackageParserException { + final ArrayList<ApkAssets> assets = new ArrayList<>(); + + // Include parent ApkAssets. if (parentSplitIdx >= 0) { - Collections.addAll(assetPaths, mCachedPaths[parentSplitIdx]); + Collections.addAll(assets, mCachedSplitApks[parentSplitIdx]); } - assetPaths.add(mSplitPaths[splitIdx]); + // Include this ApkAssets. + assets.add(loadApkAssets(mSplitPaths[splitIdx], mFlags)); + + // Load and include all config splits for this feature. for (int configSplitIdx : configSplitIndices) { - assetPaths.add(mSplitPaths[configSplitIdx]); + assets.add(loadApkAssets(mSplitPaths[configSplitIdx], mFlags)); } - mCachedPaths[splitIdx] = assetPaths.toArray(new String[assetPaths.size()]); - mCachedAssetManagers[splitIdx] = createAssetManagerWithPaths(mCachedPaths[splitIdx], - mFlags); + + // Cache the results. + mCachedSplitApks[splitIdx] = assets.toArray(new ApkAssets[assets.size()]); + mCachedAssetManagers[splitIdx] = createAssetManagerWithAssets(mCachedSplitApks[splitIdx]); } @Override - public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException { + public AssetManager getBaseAssetManager() throws PackageParserException { loadDependenciesForSplit(0); return mCachedAssetManagers[0]; } @Override - public AssetManager getSplitAssetManager(int idx) throws PackageParser.PackageParserException { + public AssetManager getSplitAssetManager(int idx) throws PackageParserException { // Since we insert the base at position 0, and PackageParser keeps splits separate from // the base, we need to adjust the index. loadDependenciesForSplit(idx + 1); diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java new file mode 100644 index 000000000000..9de8be3e86af --- /dev/null +++ b/core/java/android/content/res/ApkAssets.java @@ -0,0 +1,195 @@ +/* + * 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.content.res; + +import android.annotation.NonNull; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; + +import java.io.FileDescriptor; +import java.io.IOException; + +/** + * The loaded, immutable, in-memory representation of an APK. + * + * The main implementation is native C++ and there is very little API surface exposed here. The APK + * is mainly accessed via {@link AssetManager}. + * + * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers, + * making the creation of AssetManagers very cheap. + * @hide + */ +public final class ApkAssets { + @GuardedBy("this") private final long mNativePtr; + @GuardedBy("this") private StringBlock mStringBlock; + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException { + return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @param system When true, the APK is loaded as a system APK (framework). + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system) + throws IOException { + return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given path on disk. + * + * @param path The path to an APK on disk. + * @param system When true, the APK is loaded as a system APK (framework). + * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are + * loaded as a shared library. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system, + boolean forceSharedLibrary) throws IOException { + return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/); + } + + /** + * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications. + * + * Performs a dup of the underlying fd, so you must take care of still closing + * the FileDescriptor yourself (and can do that whenever you want). + * + * @param fd The FileDescriptor of an open, readable APK. + * @param friendlyName The friendly name used to identify this ApkAssets when logging. + * @param system When true, the APK is loaded as a system APK (framework). + * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are + * loaded as a shared library. + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd, + @NonNull String friendlyName, boolean system, boolean forceSharedLibrary) + throws IOException { + return new ApkAssets(fd, friendlyName, system, forceSharedLibrary); + } + + /** + * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path + * is encoded within the IDMAP. + * + * @param idmapPath Path to the IDMAP of an overlay APK. + * @param system When true, the APK is loaded as a system APK (framework). + * @return a new instance of ApkAssets. + * @throws IOException if a disk I/O error or parsing error occurred. + */ + public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system) + throws IOException { + return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/); + } + + private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) + throws IOException { + Preconditions.checkNotNull(path, "path"); + mNativePtr = nativeLoad(path, system, forceSharedLib, overlay); + mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); + } + + private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system, + boolean forceSharedLib) throws IOException { + Preconditions.checkNotNull(fd, "fd"); + Preconditions.checkNotNull(friendlyName, "friendlyName"); + mNativePtr = nativeLoadFromFd(fd, friendlyName, system, forceSharedLib); + mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); + } + + public @NonNull String getAssetPath() { + synchronized (this) { + return nativeGetAssetPath(mNativePtr); + } + } + + CharSequence getStringFromPool(int idx) { + synchronized (this) { + return mStringBlock.get(idx); + } + } + + /** + * Retrieve a parser for a compiled XML file. This is associated with a single APK and + * <em>NOT</em> a full AssetManager. This means that shared-library references will not be + * dynamically assigned runtime package IDs. + * + * @param fileName The path to the file within the APK. + * @return An XmlResourceParser. + * @throws IOException if the file was not found or an error occurred retrieving it. + */ + public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException { + Preconditions.checkNotNull(fileName, "fileName"); + synchronized (this) { + long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName); + try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) { + XmlResourceParser parser = block.newParser(); + // If nativeOpenXml doesn't throw, it will always return a valid native pointer, + // which makes newParser always return non-null. But let's be paranoid. + if (parser == null) { + throw new AssertionError("block.newParser() returned a null parser"); + } + return parser; + } + } + } + + /** + * Returns false if the underlying APK was changed since this ApkAssets was loaded. + */ + public boolean isUpToDate() { + synchronized (this) { + return nativeIsUpToDate(mNativePtr); + } + } + + @Override + public String toString() { + return "ApkAssets{path=" + getAssetPath() + "}"; + } + + @Override + protected void finalize() throws Throwable { + nativeDestroy(mNativePtr); + } + + private static native long nativeLoad( + @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay) + throws IOException; + private static native long nativeLoadFromFd(@NonNull FileDescriptor fd, + @NonNull String friendlyName, boolean system, boolean forceSharedLib) + throws IOException; + private static native void nativeDestroy(long ptr); + private static native @NonNull String nativeGetAssetPath(long ptr); + private static native long nativeGetStringBlock(long ptr); + private static native boolean nativeIsUpToDate(long ptr); + private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException; +} diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 5f8a34d46ecd..289534273d13 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -18,22 +18,33 @@ package android.content.res; import android.annotation.AnyRes; import android.annotation.ArrayRes; +import android.annotation.AttrRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringRes; +import android.annotation.StyleRes; import android.content.pm.ActivityInfo; import android.content.res.Configuration.NativeConfig; import android.os.ParcelFileDescriptor; +import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; -import java.io.FileDescriptor; +import libcore.io.IoUtils; + +import java.io.BufferedReader; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.channels.FileLock; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; /** @@ -44,7 +55,20 @@ import java.util.HashMap; * bytes. */ public final class AssetManager implements AutoCloseable { - /* modes used when opening an asset */ + private static final String TAG = "AssetManager"; + private static final boolean DEBUG_REFS = false; + + private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk"; + + private static final Object sSync = new Object(); + + private static final ApkAssets[] sEmptyApkAssets = new ApkAssets[0]; + + // Not private for LayoutLib's BridgeAssetManager. + @GuardedBy("sSync") static AssetManager sSystem = null; + + @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0]; + @GuardedBy("sSync") private static ArraySet<ApkAssets> sSystemApkAssetsSet; /** * Mode for {@link #open(String, int)}: no specific information about how @@ -67,88 +91,392 @@ public final class AssetManager implements AutoCloseable { */ public static final int ACCESS_BUFFER = 3; - private static final String TAG = "AssetManager"; - private static final boolean localLOGV = false || false; - - private static final boolean DEBUG_REFS = false; - - private static final Object sSync = new Object(); - /*package*/ static AssetManager sSystem = null; + @GuardedBy("this") private final TypedValue mValue = new TypedValue(); + @GuardedBy("this") private final long[] mOffsets = new long[2]; - private final TypedValue mValue = new TypedValue(); - private final long[] mOffsets = new long[2]; - - // For communication with native code. - private long mObject; + // Pointer to native implementation, stuffed inside a long. + @GuardedBy("this") private long mObject; + + // The loaded asset paths. + @GuardedBy("this") private ApkAssets[] mApkAssets; + + // Debug/reference counting implementation. + @GuardedBy("this") private boolean mOpen = true; + @GuardedBy("this") private int mNumRefs = 1; + @GuardedBy("this") private HashMap<Long, RuntimeException> mRefStacks; + + /** + * A Builder class that helps create an AssetManager with only a single invocation of + * {@link AssetManager#setApkAssets(ApkAssets[], boolean)}. Without using this builder, + * AssetManager must ensure there are system ApkAssets loaded at all times, which when combined + * with the user's call to add additional ApkAssets, results in multiple calls to + * {@link AssetManager#setApkAssets(ApkAssets[], boolean)}. + * @hide + */ + public static class Builder { + private ArrayList<ApkAssets> mUserApkAssets = new ArrayList<>(); + + public Builder addApkAssets(ApkAssets apkAssets) { + mUserApkAssets.add(apkAssets); + return this; + } + + public AssetManager build() { + // Retrieving the system ApkAssets forces their creation as well. + final ApkAssets[] systemApkAssets = getSystem().getApkAssets(); + + final int totalApkAssetCount = systemApkAssets.length + mUserApkAssets.size(); + final ApkAssets[] apkAssets = new ApkAssets[totalApkAssetCount]; + + System.arraycopy(systemApkAssets, 0, apkAssets, 0, systemApkAssets.length); + + final int userApkAssetCount = mUserApkAssets.size(); + for (int i = 0; i < userApkAssetCount; i++) { + apkAssets[i + systemApkAssets.length] = mUserApkAssets.get(i); + } + + // Calling this constructor prevents creation of system ApkAssets, which we took care + // of in this Builder. + final AssetManager assetManager = new AssetManager(false /*sentinel*/); + assetManager.mApkAssets = apkAssets; + AssetManager.nativeSetApkAssets(assetManager.mObject, apkAssets, + false /*invalidateCaches*/); + return assetManager; + } + } - private StringBlock mStringBlocks[] = null; - - private int mNumRefs = 1; - private boolean mOpen = true; - private HashMap<Long, RuntimeException> mRefStacks; - /** * Create a new AssetManager containing only the basic system assets. * Applications will not generally use this method, instead retrieving the * appropriate asset manager with {@link Resources#getAssets}. Not for * use by applications. - * {@hide} + * @hide */ public AssetManager() { - synchronized (this) { - if (DEBUG_REFS) { - mNumRefs = 0; - incRefsLocked(this.hashCode()); - } - init(false); - if (localLOGV) Log.v(TAG, "New asset manager: " + this); - ensureSystemAssets(); + final ApkAssets[] assets; + synchronized (sSync) { + createSystemAssetsInZygoteLocked(); + assets = sSystemApkAssets; } - } - private static void ensureSystemAssets() { - synchronized (sSync) { - if (sSystem == null) { - AssetManager system = new AssetManager(true); - system.makeStringBlocks(null); - sSystem = system; - } + mObject = nativeCreate(); + if (DEBUG_REFS) { + mNumRefs = 0; + incRefsLocked(hashCode()); } + + // Always set the framework resources. + setApkAssets(assets, false /*invalidateCaches*/); } - - private AssetManager(boolean isSystem) { + + /** + * Private constructor that doesn't call ensureSystemAssets. + * Used for the creation of system assets. + */ + @SuppressWarnings("unused") + private AssetManager(boolean sentinel) { + mObject = nativeCreate(); if (DEBUG_REFS) { - synchronized (this) { - mNumRefs = 0; - incRefsLocked(this.hashCode()); + mNumRefs = 0; + incRefsLocked(hashCode()); + } + } + + /** + * This must be called from Zygote so that system assets are shared by all applications. + */ + @GuardedBy("sSync") + private static void createSystemAssetsInZygoteLocked() { + if (sSystem != null) { + return; + } + + // Make sure that all IDMAPs are up to date. + nativeVerifySystemIdmaps(); + + try { + final ArrayList<ApkAssets> apkAssets = new ArrayList<>(); + apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/)); + loadStaticRuntimeOverlays(apkAssets); + + sSystemApkAssetsSet = new ArraySet<>(apkAssets); + sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]); + sSystem = new AssetManager(true /*sentinel*/); + sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/); + } catch (IOException e) { + throw new IllegalStateException("Failed to create system AssetManager", e); + } + } + + /** + * Loads the static runtime overlays declared in /data/resource-cache/overlays.list. + * Throws an exception if the file is corrupt or if loading the APKs referenced by the file + * fails. Returns quietly if the overlays.list file doesn't exist. + * @param outApkAssets The list to fill with the loaded ApkAssets. + */ + private static void loadStaticRuntimeOverlays(ArrayList<ApkAssets> outApkAssets) + throws IOException { + final FileInputStream fis; + try { + fis = new FileInputStream("/data/resource-cache/overlays.list"); + } catch (FileNotFoundException e) { + // We might not have any overlays, this is fine. We catch here since ApkAssets + // loading can also fail with the same exception, which we would want to propagate. + Log.i(TAG, "no overlays.list file found"); + return; + } + + try { + // Acquire a lock so that any idmap scanning doesn't impact the current set. + // The order of this try-with-resources block matters. We must release the lock, and + // then close the file streams when exiting the block. + try (final BufferedReader br = new BufferedReader(new InputStreamReader(fis)); + final FileLock flock = fis.getChannel().lock(0, Long.MAX_VALUE, true /*shared*/)) { + for (String line; (line = br.readLine()) != null; ) { + final String idmapPath = line.split(" ")[1]; + outApkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/)); + } } + } finally { + // When BufferedReader is closed above, FileInputStream is closed as well. But let's be + // paranoid. + IoUtils.closeQuietly(fis); } - init(true); - if (localLOGV) Log.v(TAG, "New asset manager: " + this); } /** * Return a global shared asset manager that provides access to only * system assets (no application assets). - * {@hide} + * @hide */ public static AssetManager getSystem() { - ensureSystemAssets(); - return sSystem; + synchronized (sSync) { + createSystemAssetsInZygoteLocked(); + return sSystem; + } } /** * Close this asset manager. */ + @Override public void close() { - synchronized(this) { - //System.out.println("Release: num=" + mNumRefs - // + ", released=" + mReleased); + synchronized (this) { + if (!mOpen) { + return; + } + + mOpen = false; + decRefsLocked(hashCode()); + } + } + + /** + * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)} + * family of methods. + * + * @param apkAssets The new set of paths. + * @param invalidateCaches Whether to invalidate any caches. This should almost always be true. + * Set this to false if you are appending new resources + * (not new configurations). + * @hide + */ + public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) { + Preconditions.checkNotNull(apkAssets, "apkAssets"); + + ApkAssets[] newApkAssets = new ApkAssets[sSystemApkAssets.length + apkAssets.length]; + + // Copy the system assets first. + System.arraycopy(sSystemApkAssets, 0, newApkAssets, 0, sSystemApkAssets.length); + + // Copy the given ApkAssets if they are not already in the system list. + int newLength = sSystemApkAssets.length; + for (ApkAssets apkAsset : apkAssets) { + if (!sSystemApkAssetsSet.contains(apkAsset)) { + newApkAssets[newLength++] = apkAsset; + } + } + + // Truncate if necessary. + if (newLength != newApkAssets.length) { + newApkAssets = Arrays.copyOf(newApkAssets, newLength); + } + + synchronized (this) { + ensureOpenLocked(); + mApkAssets = newApkAssets; + nativeSetApkAssets(mObject, mApkAssets, invalidateCaches); + if (invalidateCaches) { + // Invalidate all caches. + invalidateCachesLocked(-1); + } + } + } + + /** + * Invalidates the caches in this AssetManager according to the bitmask `diff`. + * + * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}. + * @see ActivityInfo.Config + */ + private void invalidateCachesLocked(int diff) { + // TODO(adamlesinski): Currently there are no caches to invalidate in Java code. + } + + /** + * Returns the set of ApkAssets loaded by this AssetManager. If the AssetManager is closed, this + * returns a 0-length array. + * @hide + */ + public @NonNull ApkAssets[] getApkAssets() { + synchronized (this) { if (mOpen) { - mOpen = false; - decRefsLocked(this.hashCode()); + return mApkAssets; } } + return sEmptyApkAssets; + } + + /** + * Returns a cookie for use with the other APIs of AssetManager. + * @return 0 if the path was not found, otherwise a positive integer cookie representing + * this path in the AssetManager. + * @hide + */ + public int findCookieForPath(@NonNull String path) { + Preconditions.checkNotNull(path, "path"); + synchronized (this) { + ensureValidLocked(); + final int count = mApkAssets.length; + for (int i = 0; i < count; i++) { + if (path.equals(mApkAssets[i].getAssetPath())) { + return i + 1; + } + } + } + return 0; + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addAssetPath(String path) { + return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/); + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addAssetPathAsSharedLibrary(String path) { + return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/); + } + + /** + * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)} + * @hide + */ + @Deprecated + public int addOverlayPath(String path) { + return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/); + } + + private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) { + Preconditions.checkNotNull(path, "path"); + synchronized (this) { + ensureOpenLocked(); + final int count = mApkAssets.length; + + // See if we already have it loaded. + for (int i = 0; i < count; i++) { + if (mApkAssets[i].getAssetPath().equals(path)) { + return i + 1; + } + } + + final ApkAssets assets; + try { + if (overlay) { + // TODO(b/70343104): This hardcoded path will be removed once + // addAssetPathInternal is deleted. + final String idmapPath = "/data/resource-cache/" + + path.substring(1).replace('/', '@') + + "@idmap"; + assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/); + } else { + assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib); + } + } catch (IOException e) { + return 0; + } + + mApkAssets = Arrays.copyOf(mApkAssets, count + 1); + mApkAssets[count] = assets; + nativeSetApkAssets(mObject, mApkAssets, true); + invalidateCachesLocked(-1); + return count + 1; + } + } + + /** + * Ensures that the native implementation has not been destroyed. + * The AssetManager may have been closed, but references to it still exist + * and therefore the native implementation is not destroyed. + */ + @GuardedBy("this") + private void ensureValidLocked() { + if (mObject == 0) { + throw new RuntimeException("AssetManager has been destroyed"); + } + } + + /** + * Ensures that the AssetManager has not been explicitly closed. If this method passes, + * then this implies that ensureValidLocked() also passes. + */ + @GuardedBy("this") + private void ensureOpenLocked() { + // If mOpen is true, this implies that mObject != 0. + if (!mOpen) { + throw new RuntimeException("AssetManager has been closed"); + } + } + + /** + * Populates {@code outValue} with the data associated a particular + * resource identifier for the current configuration. + * + * @param resId the resource identifier to load + * @param densityDpi the density bucket for which to load the resource + * @param outValue the typed value in which to put the data + * @param resolveRefs {@code true} to resolve references, {@code false} + * to leave them unresolved + * @return {@code true} if the data was loaded into {@code outValue}, + * {@code false} otherwise + */ + boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, + boolean resolveRefs) { + Preconditions.checkNotNull(outValue, "outValue"); + synchronized (this) { + ensureValidLocked(); + final int cookie = nativeGetResourceValue( + mObject, resId, (short) densityDpi, outValue, resolveRefs); + if (cookie <= 0) { + return false; + } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); + } + return true; + } } /** @@ -158,8 +486,7 @@ public final class AssetManager implements AutoCloseable { * @param resId the resource identifier to load * @return the string value, or {@code null} */ - @Nullable - final CharSequence getResourceText(@StringRes int resId) { + @Nullable CharSequence getResourceText(@StringRes int resId) { synchronized (this) { final TypedValue outValue = mValue; if (getResourceValue(resId, 0, outValue, true)) { @@ -174,15 +501,15 @@ public final class AssetManager implements AutoCloseable { * identifier for the current configuration. * * @param resId the resource identifier to load - * @param bagEntryId + * @param bagEntryId the index into the bag to load * @return the string value, or {@code null} */ - @Nullable - final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { + @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) { synchronized (this) { + ensureValidLocked(); final TypedValue outValue = mValue; - final int block = loadResourceBagValue(resId, bagEntryId, outValue, true); - if (block < 0) { + final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue); + if (cookie <= 0) { return null; } @@ -191,52 +518,60 @@ public final class AssetManager implements AutoCloseable { outValue.changingConfigurations); if (outValue.type == TypedValue.TYPE_STRING) { - return mStringBlocks[block].get(outValue.data); + return mApkAssets[cookie - 1].getStringFromPool(outValue.data); } return outValue.coerceToString(); } } + int getResourceArraySize(@ArrayRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceArraySize(mObject, resId); + } + } + /** - * Retrieves the string array associated with a particular resource - * identifier for the current configuration. + * Populates `outData` with array elements of `resId`. `outData` is normally + * used with + * {@link TypedArray}. * - * @param resId the resource identifier of the string array - * @return the string array, or {@code null} + * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES} + * long, + * with the indices of the data representing the type, value, asset cookie, + * resource ID, + * configuration change mask, and density of the element. + * + * @param resId The resource ID of an array resource. + * @param outData The array to populate with data. + * @return The length of the array. + * + * @see TypedArray#STYLE_TYPE + * @see TypedArray#STYLE_DATA + * @see TypedArray#STYLE_ASSET_COOKIE + * @see TypedArray#STYLE_RESOURCE_ID + * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS + * @see TypedArray#STYLE_DENSITY */ - @Nullable - final String[] getResourceStringArray(@ArrayRes int resId) { - return getArrayStringResource(resId); + int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) { + Preconditions.checkNotNull(outData, "outData"); + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceArray(mObject, resId, outData); + } } /** - * Populates {@code outValue} with the data associated a particular - * resource identifier for the current configuration. + * Retrieves the string array associated with a particular resource + * identifier for the current configuration. * - * @param resId the resource identifier to load - * @param densityDpi the density bucket for which to load the resource - * @param outValue the typed value in which to put the data - * @param resolveRefs {@code true} to resolve references, {@code false} - * to leave them unresolved - * @return {@code true} if the data was loaded into {@code outValue}, - * {@code false} otherwise + * @param resId the resource identifier of the string array + * @return the string array, or {@code null} */ - final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, - boolean resolveRefs) { + @Nullable String[] getResourceStringArray(@ArrayRes int resId) { synchronized (this) { - final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs); - if (block < 0) { - return false; - } - - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); - - if (outValue.type == TypedValue.TYPE_STRING) { - outValue.string = mStringBlocks[block].get(outValue.data); - } - return true; + ensureValidLocked(); + return nativeGetResourceStringArray(mObject, resId); } } @@ -246,26 +581,48 @@ public final class AssetManager implements AutoCloseable { * * @param resId the resource id of the string array */ - final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { + @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) { synchronized (this) { - final int[] rawInfoArray = getArrayStringInfo(resId); + ensureValidLocked(); + final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId); if (rawInfoArray == null) { return null; } + final int rawInfoArrayLen = rawInfoArray.length; final int infoArrayLen = rawInfoArrayLen / 2; - int block; - int index; final CharSequence[] retArray = new CharSequence[infoArrayLen]; for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) { - block = rawInfoArray[i]; - index = rawInfoArray[i + 1]; - retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null; + int cookie = rawInfoArray[i]; + int index = rawInfoArray[i + 1]; + retArray[j] = (index >= 0 && cookie > 0) + ? mApkAssets[cookie - 1].getStringFromPool(index) : null; } return retArray; } } + @Nullable int[] getResourceIntArray(@ArrayRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceIntArray(mObject, resId); + } + } + + /** + * Get the attributes for a style resource. These are the <item> + * elements in + * a <style> resource. + * @param resId The resource ID of the style + * @return An array of attribute IDs. + */ + @AttrRes int[] getStyleAttributes(@StyleRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetStyleAttributes(mObject, resId); + } + } + /** * Populates {@code outValue} with the data associated with a particular * resource identifier for the current configuration. Resolves theme @@ -279,73 +636,88 @@ public final class AssetManager implements AutoCloseable { * @return {@code true} if the data was loaded into {@code outValue}, * {@code false} otherwise */ - final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, + boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue, boolean resolveRefs) { - final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs); - if (block < 0) { - return false; + Preconditions.checkNotNull(outValue, "outValue"); + synchronized (this) { + ensureValidLocked(); + final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue, + resolveRefs); + if (cookie <= 0) { + return false; + } + + // Convert the changing configurations flags populated by native code. + outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( + outValue.changingConfigurations); + + if (outValue.type == TypedValue.TYPE_STRING) { + outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data); + } + return true; } + } - // Convert the changing configurations flags populated by native code. - outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - outValue.changingConfigurations); + void dumpTheme(long theme, int priority, String tag, String prefix) { + synchronized (this) { + ensureValidLocked(); + nativeThemeDump(mObject, theme, priority, tag, prefix); + } + } - if (outValue.type == TypedValue.TYPE_STRING) { - final StringBlock[] blocks = ensureStringBlocks(); - outValue.string = blocks[block].get(outValue.data); + @Nullable String getResourceName(@AnyRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceName(mObject, resId); } - return true; } - /** - * Ensures the string blocks are loaded. - * - * @return the string blocks - */ - @NonNull - final StringBlock[] ensureStringBlocks() { + @Nullable String getResourcePackageName(@AnyRes int resId) { synchronized (this) { - if (mStringBlocks == null) { - makeStringBlocks(sSystem.mStringBlocks); - } - return mStringBlocks; + ensureValidLocked(); + return nativeGetResourcePackageName(mObject, resId); } } - /*package*/ final void makeStringBlocks(StringBlock[] seed) { - final int seedNum = (seed != null) ? seed.length : 0; - final int num = getStringBlockCount(); - mStringBlocks = new StringBlock[num]; - if (localLOGV) Log.v(TAG, "Making string blocks for " + this - + ": " + num); - for (int i=0; i<num; i++) { - if (i < seedNum) { - mStringBlocks[i] = seed[i]; - } else { - mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true); - } + @Nullable String getResourceTypeName(@AnyRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceTypeName(mObject, resId); + } + } + + @Nullable String getResourceEntryName(@AnyRes int resId) { + synchronized (this) { + ensureValidLocked(); + return nativeGetResourceEntryName(mObject, resId); } } - /*package*/ final CharSequence getPooledStringForCookie(int cookie, int id) { + @AnyRes int getResourceIdentifier(@NonNull String name, @Nullable String defType, + @Nullable String defPackage) { synchronized (this) { - // Cookies map to string blocks starting at 1. - return mStringBlocks[cookie - 1].get(id); + ensureValidLocked(); + // name is checked in JNI. + return nativeGetResourceIdentifier(mObject, name, defType, defPackage); } } + CharSequence getPooledStringForCookie(int cookie, int id) { + // Cookies map to ApkAssets starting at 1. + return getApkAssets()[cookie - 1].getStringFromPool(id); + } + /** * Open an asset using ACCESS_STREAMING mode. This provides access to * files that have been bundled with an application as assets -- that is, * files placed in to the "assets" directory. * - * @param fileName The name of the asset to open. This name can be - * hierarchical. + * @param fileName The name of the asset to open. This name can be hierarchical. * * @see #open(String, int) * @see #list */ - public final InputStream open(String fileName) throws IOException { + public @NonNull InputStream open(@NonNull String fileName) throws IOException { return open(fileName, ACCESS_STREAMING); } @@ -355,8 +727,7 @@ public final class AssetManager implements AutoCloseable { * with an application as assets -- that is, files placed in to the * "assets" directory. * - * @param fileName The name of the asset to open. This name can be - * hierarchical. + * @param fileName The name of the asset to open. This name can be hierarchical. * @param accessMode Desired access mode for retrieving the data. * * @see #ACCESS_UNKNOWN @@ -366,34 +737,40 @@ public final class AssetManager implements AutoCloseable { * @see #open(String) * @see #list */ - public final InputStream open(String fileName, int accessMode) - throws IOException { + public @NonNull InputStream open(@NonNull String fileName, int accessMode) throws IOException { + Preconditions.checkNotNull(fileName, "fileName"); synchronized (this) { - if (!mOpen) { - throw new RuntimeException("Assetmanager has been closed"); - } - long asset = openAsset(fileName, accessMode); - if (asset != 0) { - AssetInputStream res = new AssetInputStream(asset); - incRefsLocked(res.hashCode()); - return res; + ensureOpenLocked(); + final long asset = nativeOpenAsset(mObject, fileName, accessMode); + if (asset == 0) { + throw new FileNotFoundException("Asset file: " + fileName); } + final AssetInputStream assetInputStream = new AssetInputStream(asset); + incRefsLocked(assetInputStream.hashCode()); + return assetInputStream; } - throw new FileNotFoundException("Asset file: " + fileName); } - public final AssetFileDescriptor openFd(String fileName) - throws IOException { + /** + * Open an uncompressed asset by mmapping it and returning an {@link AssetFileDescriptor}. + * This provides access to files that have been bundled with an application as assets -- that + * is, files placed in to the "assets" directory. + * + * The asset must be uncompressed, or an exception will be thrown. + * + * @param fileName The name of the asset to open. This name can be hierarchical. + * @return An open AssetFileDescriptor. + */ + public @NonNull AssetFileDescriptor openFd(@NonNull String fileName) throws IOException { + Preconditions.checkNotNull(fileName, "fileName"); synchronized (this) { - if (!mOpen) { - throw new RuntimeException("Assetmanager has been closed"); - } - ParcelFileDescriptor pfd = openAssetFd(fileName, mOffsets); - if (pfd != null) { - return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); + ensureOpenLocked(); + final ParcelFileDescriptor pfd = nativeOpenAssetFd(mObject, fileName, mOffsets); + if (pfd == null) { + throw new FileNotFoundException("Asset file: " + fileName); } + return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); } - throw new FileNotFoundException("Asset file: " + fileName); } /** @@ -408,90 +785,121 @@ public final class AssetManager implements AutoCloseable { * * @see #open */ - public native final String[] list(String path) - throws IOException; + public @Nullable String[] list(@NonNull String path) throws IOException { + Preconditions.checkNotNull(path, "path"); + synchronized (this) { + ensureValidLocked(); + return nativeList(mObject, path); + } + } /** - * {@hide} * Open a non-asset file as an asset using ACCESS_STREAMING mode. This * provides direct access to all of the files included in an application * package (not only its assets). Applications should not normally use * this. - * + * + * @param fileName Name of the asset to retrieve. + * * @see #open(String) + * @hide */ - public final InputStream openNonAsset(String fileName) throws IOException { + public @NonNull InputStream openNonAsset(@NonNull String fileName) throws IOException { return openNonAsset(0, fileName, ACCESS_STREAMING); } /** - * {@hide} * Open a non-asset file as an asset using a specific access mode. This * provides direct access to all of the files included in an application * package (not only its assets). Applications should not normally use * this. - * + * + * @param fileName Name of the asset to retrieve. + * @param accessMode Desired access mode for retrieving the data. + * + * @see #ACCESS_UNKNOWN + * @see #ACCESS_STREAMING + * @see #ACCESS_RANDOM + * @see #ACCESS_BUFFER * @see #open(String, int) + * @hide */ - public final InputStream openNonAsset(String fileName, int accessMode) - throws IOException { + public @NonNull InputStream openNonAsset(@NonNull String fileName, int accessMode) + throws IOException { return openNonAsset(0, fileName, accessMode); } /** - * {@hide} * Open a non-asset in a specified package. Not for use by applications. - * + * * @param cookie Identifier of the package to be opened. * @param fileName Name of the asset to retrieve. + * @hide */ - public final InputStream openNonAsset(int cookie, String fileName) - throws IOException { + public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName) + throws IOException { return openNonAsset(cookie, fileName, ACCESS_STREAMING); } /** - * {@hide} * Open a non-asset in a specified package. Not for use by applications. - * + * * @param cookie Identifier of the package to be opened. * @param fileName Name of the asset to retrieve. * @param accessMode Desired access mode for retrieving the data. + * @hide */ - public final InputStream openNonAsset(int cookie, String fileName, int accessMode) - throws IOException { + public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName, int accessMode) + throws IOException { + Preconditions.checkNotNull(fileName, "fileName"); synchronized (this) { - if (!mOpen) { - throw new RuntimeException("Assetmanager has been closed"); - } - long asset = openNonAssetNative(cookie, fileName, accessMode); - if (asset != 0) { - AssetInputStream res = new AssetInputStream(asset); - incRefsLocked(res.hashCode()); - return res; + ensureOpenLocked(); + final long asset = nativeOpenNonAsset(mObject, cookie, fileName, accessMode); + if (asset == 0) { + throw new FileNotFoundException("Asset absolute file: " + fileName); } + final AssetInputStream assetInputStream = new AssetInputStream(asset); + incRefsLocked(assetInputStream.hashCode()); + return assetInputStream; } - throw new FileNotFoundException("Asset absolute file: " + fileName); } - public final AssetFileDescriptor openNonAssetFd(String fileName) + /** + * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}. + * This provides direct access to all of the files included in an application + * package (not only its assets). Applications should not normally use this. + * + * The asset must not be compressed, or an exception will be thrown. + * + * @param fileName Name of the asset to retrieve. + */ + public @NonNull AssetFileDescriptor openNonAssetFd(@NonNull String fileName) throws IOException { return openNonAssetFd(0, fileName); } - - public final AssetFileDescriptor openNonAssetFd(int cookie, - String fileName) throws IOException { + + /** + * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}. + * This provides direct access to all of the files included in an application + * package (not only its assets). Applications should not normally use this. + * + * The asset must not be compressed, or an exception will be thrown. + * + * @param cookie Identifier of the package to be opened. + * @param fileName Name of the asset to retrieve. + */ + public @NonNull AssetFileDescriptor openNonAssetFd(int cookie, @NonNull String fileName) + throws IOException { + Preconditions.checkNotNull(fileName, "fileName"); synchronized (this) { - if (!mOpen) { - throw new RuntimeException("Assetmanager has been closed"); - } - ParcelFileDescriptor pfd = openNonAssetFdNative(cookie, - fileName, mOffsets); - if (pfd != null) { - return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); + ensureOpenLocked(); + final ParcelFileDescriptor pfd = + nativeOpenNonAssetFd(mObject, cookie, fileName, mOffsets); + if (pfd == null) { + throw new FileNotFoundException("Asset absolute file: " + fileName); } + return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]); } - throw new FileNotFoundException("Asset absolute file: " + fileName); } /** @@ -499,7 +907,7 @@ public final class AssetManager implements AutoCloseable { * * @param fileName The name of the file to retrieve. */ - public final XmlResourceParser openXmlResourceParser(String fileName) + public @NonNull XmlResourceParser openXmlResourceParser(@NonNull String fileName) throws IOException { return openXmlResourceParser(0, fileName); } @@ -510,270 +918,265 @@ public final class AssetManager implements AutoCloseable { * @param cookie Identifier of the package to be opened. * @param fileName The name of the file to retrieve. */ - public final XmlResourceParser openXmlResourceParser(int cookie, - String fileName) throws IOException { - XmlBlock block = openXmlBlockAsset(cookie, fileName); - XmlResourceParser rp = block.newParser(); - block.close(); - return rp; + public @NonNull XmlResourceParser openXmlResourceParser(int cookie, @NonNull String fileName) + throws IOException { + try (XmlBlock block = openXmlBlockAsset(cookie, fileName)) { + XmlResourceParser parser = block.newParser(); + // If openXmlBlockAsset doesn't throw, it will always return an XmlBlock object with + // a valid native pointer, which makes newParser always return non-null. But let's + // be paranoid. + if (parser == null) { + throw new AssertionError("block.newParser() returned a null parser"); + } + return parser; + } } /** - * {@hide} - * Retrieve a non-asset as a compiled XML file. Not for use by - * applications. + * Retrieve a non-asset as a compiled XML file. Not for use by applications. * * @param fileName The name of the file to retrieve. + * @hide */ - /*package*/ final XmlBlock openXmlBlockAsset(String fileName) - throws IOException { + @NonNull XmlBlock openXmlBlockAsset(@NonNull String fileName) throws IOException { return openXmlBlockAsset(0, fileName); } /** - * {@hide} * Retrieve a non-asset as a compiled XML file. Not for use by * applications. * * @param cookie Identifier of the package to be opened. * @param fileName Name of the asset to retrieve. + * @hide */ - /*package*/ final XmlBlock openXmlBlockAsset(int cookie, String fileName) - throws IOException { + @NonNull XmlBlock openXmlBlockAsset(int cookie, @NonNull String fileName) throws IOException { + Preconditions.checkNotNull(fileName, "fileName"); synchronized (this) { - if (!mOpen) { - throw new RuntimeException("Assetmanager has been closed"); - } - long xmlBlock = openXmlAssetNative(cookie, fileName); - if (xmlBlock != 0) { - XmlBlock res = new XmlBlock(this, xmlBlock); - incRefsLocked(res.hashCode()); - return res; + ensureOpenLocked(); + final long xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName); + if (xmlBlock == 0) { + throw new FileNotFoundException("Asset XML file: " + fileName); } + final XmlBlock block = new XmlBlock(this, xmlBlock); + incRefsLocked(block.hashCode()); + return block; } - throw new FileNotFoundException("Asset XML file: " + fileName); } - /*package*/ void xmlBlockGone(int id) { + void xmlBlockGone(int id) { synchronized (this) { decRefsLocked(id); } } - /*package*/ final long createTheme() { + void applyStyle(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, + @Nullable XmlBlock.Parser parser, @NonNull int[] inAttrs, long outValuesAddress, + long outIndicesAddress) { + Preconditions.checkNotNull(inAttrs, "inAttrs"); synchronized (this) { - if (!mOpen) { - throw new RuntimeException("Assetmanager has been closed"); - } - long res = newTheme(); - incRefsLocked(res); - return res; + // Need to synchronize on AssetManager because we will be accessing + // the native implementation of AssetManager. + ensureValidLocked(); + nativeApplyStyle(mObject, themePtr, defStyleAttr, defStyleRes, + parser != null ? parser.mParseState : 0, inAttrs, outValuesAddress, + outIndicesAddress); + } + } + + boolean resolveAttrs(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes, + @Nullable int[] inValues, @NonNull int[] inAttrs, @NonNull int[] outValues, + @NonNull int[] outIndices) { + Preconditions.checkNotNull(inAttrs, "inAttrs"); + Preconditions.checkNotNull(outValues, "outValues"); + Preconditions.checkNotNull(outIndices, "outIndices"); + synchronized (this) { + // Need to synchronize on AssetManager because we will be accessing + // the native implementation of AssetManager. + ensureValidLocked(); + return nativeResolveAttrs(mObject, + themePtr, defStyleAttr, defStyleRes, inValues, inAttrs, outValues, outIndices); + } + } + + boolean retrieveAttributes(@NonNull XmlBlock.Parser parser, @NonNull int[] inAttrs, + @NonNull int[] outValues, @NonNull int[] outIndices) { + Preconditions.checkNotNull(parser, "parser"); + Preconditions.checkNotNull(inAttrs, "inAttrs"); + Preconditions.checkNotNull(outValues, "outValues"); + Preconditions.checkNotNull(outIndices, "outIndices"); + synchronized (this) { + // Need to synchronize on AssetManager because we will be accessing + // the native implementation of AssetManager. + ensureValidLocked(); + return nativeRetrieveAttributes( + mObject, parser.mParseState, inAttrs, outValues, outIndices); } } - /*package*/ final void releaseTheme(long theme) { + long createTheme() { synchronized (this) { - deleteTheme(theme); - decRefsLocked(theme); + ensureValidLocked(); + long themePtr = nativeThemeCreate(mObject); + incRefsLocked(themePtr); + return themePtr; } } + void releaseTheme(long themePtr) { + synchronized (this) { + nativeThemeDestroy(themePtr); + decRefsLocked(themePtr); + } + } + + void applyStyleToTheme(long themePtr, @StyleRes int resId, boolean force) { + synchronized (this) { + // Need to synchronize on AssetManager because we will be accessing + // the native implementation of AssetManager. + ensureValidLocked(); + nativeThemeApplyStyle(mObject, themePtr, resId, force); + } + } + + @Override protected void finalize() throws Throwable { - try { - if (DEBUG_REFS && mNumRefs != 0) { - Log.w(TAG, "AssetManager " + this - + " finalized with non-zero refs: " + mNumRefs); - if (mRefStacks != null) { - for (RuntimeException e : mRefStacks.values()) { - Log.w(TAG, "Reference from here", e); - } + if (DEBUG_REFS && mNumRefs != 0) { + Log.w(TAG, "AssetManager " + this + " finalized with non-zero refs: " + mNumRefs); + if (mRefStacks != null) { + for (RuntimeException e : mRefStacks.values()) { + Log.w(TAG, "Reference from here", e); } } - destroy(); - } finally { - super.finalize(); + } + + if (mObject != 0) { + nativeDestroy(mObject); } } - + + /* No Locking is needed for AssetInputStream because an AssetInputStream is not-thread + safe and it does not rely on AssetManager once it has been created. It completely owns the + underlying Asset. */ public final class AssetInputStream extends InputStream { + private long mAssetNativePtr; + private long mLength; + private long mMarkPos; + /** * @hide */ public final int getAssetInt() { throw new UnsupportedOperationException(); } + /** * @hide */ public final long getNativeAsset() { - return mAsset; + return mAssetNativePtr; } - private AssetInputStream(long asset) - { - mAsset = asset; - mLength = getAssetLength(asset); + + private AssetInputStream(long assetNativePtr) { + mAssetNativePtr = assetNativePtr; + mLength = nativeAssetGetLength(assetNativePtr); } + + @Override public final int read() throws IOException { - return readAssetChar(mAsset); - } - public final boolean markSupported() { - return true; - } - public final int available() throws IOException { - long len = getAssetRemainingLength(mAsset); - return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len; - } - public final void close() throws IOException { - synchronized (AssetManager.this) { - if (mAsset != 0) { - destroyAsset(mAsset); - mAsset = 0; - decRefsLocked(hashCode()); - } - } - } - public final void mark(int readlimit) { - mMarkPos = seekAsset(mAsset, 0, 0); + ensureOpen(); + return nativeAssetReadChar(mAssetNativePtr); } - public final void reset() throws IOException { - seekAsset(mAsset, mMarkPos, -1); - } - public final int read(byte[] b) throws IOException { - return readAsset(mAsset, b, 0, b.length); + + @Override + public final int read(@NonNull byte[] b) throws IOException { + ensureOpen(); + Preconditions.checkNotNull(b, "b"); + return nativeAssetRead(mAssetNativePtr, b, 0, b.length); } - public final int read(byte[] b, int off, int len) throws IOException { - return readAsset(mAsset, b, off, len); + + @Override + public final int read(@NonNull byte[] b, int off, int len) throws IOException { + ensureOpen(); + Preconditions.checkNotNull(b, "b"); + return nativeAssetRead(mAssetNativePtr, b, off, len); } + + @Override public final long skip(long n) throws IOException { - long pos = seekAsset(mAsset, 0, 0); - if ((pos+n) > mLength) { - n = mLength-pos; + ensureOpen(); + long pos = nativeAssetSeek(mAssetNativePtr, 0, 0); + if ((pos + n) > mLength) { + n = mLength - pos; } if (n > 0) { - seekAsset(mAsset, n, 0); + nativeAssetSeek(mAssetNativePtr, n, 0); } return n; } - protected void finalize() throws Throwable - { - close(); + @Override + public final int available() throws IOException { + ensureOpen(); + final long len = nativeAssetGetRemainingLength(mAssetNativePtr); + return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len; } - private long mAsset; - private long mLength; - private long mMarkPos; - } - - /** - * Add an additional set of assets to the asset manager. This can be - * either a directory or ZIP file. Not for use by applications. Returns - * the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public final int addAssetPath(String path) { - return addAssetPathInternal(path, false); - } - - /** - * Add an application assets to the asset manager and loading it as shared library. - * This can be either a directory or ZIP file. Not for use by applications. Returns - * the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public final int addAssetPathAsSharedLibrary(String path) { - return addAssetPathInternal(path, true); - } - - private final int addAssetPathInternal(String path, boolean appAsLib) { - synchronized (this) { - int res = addAssetPathNative(path, appAsLib); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final boolean markSupported() { + return true; } - } - - private native final int addAssetPathNative(String path, boolean appAsLib); - - /** - * Add an additional set of assets to the asset manager from an already open - * FileDescriptor. Not for use by applications. - * This does not give full AssetManager functionality for these assets, - * since the origin of the file is not known for purposes of sharing, - * overlay resolution, and other features. However it does allow you - * to do simple access to the contents of the given fd as an apk file. - * Performs a dup of the underlying fd, so you must take care of still closing - * the FileDescriptor yourself (and can do that whenever you want). - * Returns the cookie of the added asset, or 0 on failure. - * {@hide} - */ - public int addAssetFd(FileDescriptor fd, String debugPathName) { - return addAssetFdInternal(fd, debugPathName, false); - } - private int addAssetFdInternal(FileDescriptor fd, String debugPathName, - boolean appAsLib) { - synchronized (this) { - int res = addAssetFdNative(fd, debugPathName, appAsLib); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final void mark(int readlimit) { + ensureOpen(); + mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0); } - } - - private native int addAssetFdNative(FileDescriptor fd, String debugPathName, - boolean appAsLib); - /** - * Add a set of assets to overlay an already added set of assets. - * - * This is only intended for application resources. System wide resources - * are handled before any Java code is executed. - * - * {@hide} - */ - - public final int addOverlayPath(String idmapPath) { - synchronized (this) { - int res = addOverlayPathNative(idmapPath); - makeStringBlocks(mStringBlocks); - return res; + @Override + public final void reset() throws IOException { + ensureOpen(); + nativeAssetSeek(mAssetNativePtr, mMarkPos, -1); } - } - /** - * See addOverlayPath. - * - * {@hide} - */ - public native final int addOverlayPathNative(String idmapPath); + @Override + public final void close() throws IOException { + if (mAssetNativePtr != 0) { + nativeAssetDestroy(mAssetNativePtr); + mAssetNativePtr = 0; - /** - * Add multiple sets of assets to the asset manager at once. See - * {@link #addAssetPath(String)} for more information. Returns array of - * cookies for each added asset with 0 indicating failure, or null if - * the input array of paths is null. - * {@hide} - */ - public final int[] addAssetPaths(String[] paths) { - if (paths == null) { - return null; + synchronized (AssetManager.this) { + decRefsLocked(hashCode()); + } + } } - int[] cookies = new int[paths.length]; - for (int i = 0; i < paths.length; i++) { - cookies[i] = addAssetPath(paths[i]); + @Override + protected void finalize() throws Throwable { + close(); } - return cookies; + private void ensureOpen() { + if (mAssetNativePtr == 0) { + throw new IllegalStateException("AssetInputStream is closed"); + } + } } /** * Determine whether the state in this asset manager is up-to-date with * the files on the filesystem. If false is returned, you need to * instantiate a new AssetManager class to see the new data. - * {@hide} + * @hide */ - public native final boolean isUpToDate(); + public boolean isUpToDate() { + for (ApkAssets apkAssets : getApkAssets()) { + if (!apkAssets.isUpToDate()) { + return false; + } + } + return true; + } /** * Get the locales that this asset manager contains data for. @@ -786,7 +1189,12 @@ public final class AssetManager implements AutoCloseable { * are of the form {@code ll_CC} where {@code ll} is a two letter language code, * and {@code CC} is a two letter country code. */ - public native final String[] getLocales(); + public String[] getLocales() { + synchronized (this) { + ensureValidLocked(); + return nativeGetLocales(mObject, false /*excludeSystem*/); + } + } /** * Same as getLocales(), except that locales that are only provided by the system (i.e. those @@ -796,132 +1204,58 @@ public final class AssetManager implements AutoCloseable { * assets support Cherokee and French, getLocales() would return * [Cherokee, English, French, German], while getNonSystemLocales() would return * [Cherokee, French]. - * {@hide} + * @hide */ - public native final String[] getNonSystemLocales(); - - /** {@hide} */ - public native final Configuration[] getSizeConfigurations(); + public String[] getNonSystemLocales() { + synchronized (this) { + ensureValidLocked(); + return nativeGetLocales(mObject, true /*excludeSystem*/); + } + } /** - * Change the configuation used when retrieving resources. Not for use by - * applications. - * {@hide} + * @hide */ - public native final void setConfiguration(int mcc, int mnc, String locale, - int orientation, int touchscreen, int density, int keyboard, - int keyboardHidden, int navigation, int screenWidth, int screenHeight, - int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, - int screenLayout, int uiMode, int colorMode, int majorVersion); + Configuration[] getSizeConfigurations() { + synchronized (this) { + ensureValidLocked(); + return nativeGetSizeConfigurations(mObject); + } + } /** - * Retrieve the resource identifier for the given resource name. + * Change the configuration used when retrieving resources. Not for use by + * applications. + * @hide */ - /*package*/ native final int getResourceIdentifier(String name, - String defType, - String defPackage); + public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation, + int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, + int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, + int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) { + synchronized (this) { + ensureValidLocked(); + nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density, + keyboard, keyboardHidden, navigation, screenWidth, screenHeight, + smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode, + colorMode, majorVersion); + } + } - /*package*/ native final String getResourceName(int resid); - /*package*/ native final String getResourcePackageName(int resid); - /*package*/ native final String getResourceTypeName(int resid); - /*package*/ native final String getResourceEntryName(int resid); - - private native final long openAsset(String fileName, int accessMode); - private final native ParcelFileDescriptor openAssetFd(String fileName, - long[] outOffsets) throws IOException; - private native final long openNonAssetNative(int cookie, String fileName, - int accessMode); - private native ParcelFileDescriptor openNonAssetFdNative(int cookie, - String fileName, long[] outOffsets) throws IOException; - private native final void destroyAsset(long asset); - private native final int readAssetChar(long asset); - private native final int readAsset(long asset, byte[] b, int off, int len); - private native final long seekAsset(long asset, long offset, int whence); - private native final long getAssetLength(long asset); - private native final long getAssetRemainingLength(long asset); - - /** Returns true if the resource was found, filling in mRetStringBlock and - * mRetData. */ - private native final int loadResourceValue(int ident, short density, TypedValue outValue, - boolean resolve); - /** Returns true if the resource was found, filling in mRetStringBlock and - * mRetData. */ - private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue, - boolean resolve); - /*package*/ static final int STYLE_NUM_ENTRIES = 6; - /*package*/ static final int STYLE_TYPE = 0; - /*package*/ static final int STYLE_DATA = 1; - /*package*/ static final int STYLE_ASSET_COOKIE = 2; - /*package*/ static final int STYLE_RESOURCE_ID = 3; - - /* Offset within typed data array for native changingConfigurations. */ - static final int STYLE_CHANGING_CONFIGURATIONS = 4; - - /*package*/ static final int STYLE_DENSITY = 5; - /*package*/ native static final void applyStyle(long theme, - int defStyleAttr, int defStyleRes, long xmlParser, - int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress); - /*package*/ native static final boolean resolveAttrs(long theme, - int defStyleAttr, int defStyleRes, int[] inValues, - int[] inAttrs, int[] outValues, int[] outIndices); - /*package*/ native final boolean retrieveAttributes( - long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices); - /*package*/ native final int getArraySize(int resource); - /*package*/ native final int retrieveArray(int resource, int[] outValues); - private native final int getStringBlockCount(); - private native final long getNativeStringBlock(int block); - - /** - * {@hide} - */ - public native final String getCookieName(int cookie); - - /** - * {@hide} - */ - public native final SparseArray<String> getAssignedPackageIdentifiers(); - - /** - * {@hide} - */ - public native static final int getGlobalAssetCount(); - /** - * {@hide} + * @hide */ - public native static final String getAssetAllocations(); - - /** - * {@hide} - */ - public native static final int getGlobalAssetManagerCount(); - - private native final long newTheme(); - private native final void deleteTheme(long theme); - /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force); - /*package*/ native static final void copyTheme(long dest, long source); - /*package*/ native static final void clearTheme(long theme); - /*package*/ native static final int loadThemeAttributeValue(long theme, int ident, - TypedValue outValue, - boolean resolve); - /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix); - /*package*/ native static final @NativeConfig int getThemeChangingConfigurations(long theme); - - private native final long openXmlAssetNative(int cookie, String fileName); - - private native final String[] getArrayStringResource(int arrayRes); - private native final int[] getArrayStringInfo(int arrayRes); - /*package*/ native final int[] getArrayIntResource(int arrayRes); - /*package*/ native final int[] getStyleAttributes(int themeRes); - - private native final void init(boolean isSystem); - private native final void destroy(); + public SparseArray<String> getAssignedPackageIdentifiers() { + synchronized (this) { + ensureValidLocked(); + return nativeGetAssignedPackageIdentifiers(mObject); + } + } @GuardedBy("this") - private final void incRefsLocked(long id) { + private void incRefsLocked(long id) { if (DEBUG_REFS) { if (mRefStacks == null) { - mRefStacks = new HashMap<Long, RuntimeException>(); + mRefStacks = new HashMap<>(); } RuntimeException ex = new RuntimeException(); ex.fillInStackTrace(); @@ -931,15 +1265,117 @@ public final class AssetManager implements AutoCloseable { } @GuardedBy("this") - private final void decRefsLocked(long id) { + private void decRefsLocked(long id) { if (DEBUG_REFS && mRefStacks != null) { mRefStacks.remove(id); } mNumRefs--; - //System.out.println("Dec streams: mNumRefs=" + mNumRefs - // + " mReleased=" + mReleased); - if (mNumRefs == 0) { - destroy(); + if (mNumRefs == 0 && mObject != 0) { + nativeDestroy(mObject); + mObject = 0; + mApkAssets = sEmptyApkAssets; } } + + // AssetManager setup native methods. + private static native long nativeCreate(); + private static native void nativeDestroy(long ptr); + private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, + boolean invalidateCaches); + private static native void nativeSetConfiguration(long ptr, int mcc, int mnc, + @Nullable String locale, int orientation, int touchscreen, int density, int keyboard, + int keyboardHidden, int navigation, int screenWidth, int screenHeight, + int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, + int uiMode, int colorMode, int majorVersion); + private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers( + long ptr); + + // File native methods. + private static native @Nullable String[] nativeList(long ptr, @NonNull String path) + throws IOException; + private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode); + private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr, + @NonNull String fileName, long[] outOffsets) throws IOException; + private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName, + int accessMode); + private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie, + @NonNull String fileName, @NonNull long[] outOffsets) throws IOException; + private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName); + + // Primitive resource native methods. + private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density, + @NonNull TypedValue outValue, boolean resolveReferences); + private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId, + @NonNull TypedValue outValue); + + private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr, + @StyleRes int resId); + private static native @Nullable String[] nativeGetResourceStringArray(long ptr, + @ArrayRes int resId); + private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr, + @ArrayRes int resId); + private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId); + private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId); + private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId, + @NonNull int[] outValues); + + // Resource name/ID native methods. + private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name, + @Nullable String defType, @Nullable String defPackage); + private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid); + private static native @Nullable String nativeGetResourcePackageName(long ptr, + @AnyRes int resid); + private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid); + private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid); + private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem); + private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr); + + // Style attribute retrieval native methods. + private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, + @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs, + long outValuesAddress, long outIndicesAddress); + private static native boolean nativeResolveAttrs(long ptr, long themePtr, + @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues, + @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); + private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr, + @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices); + + // Theme related native methods + private static native long nativeThemeCreate(long ptr); + private static native void nativeThemeDestroy(long themePtr); + private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId, + boolean force); + static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr); + static native void nativeThemeClear(long themePtr); + private static native int nativeThemeGetAttributeValue(long ptr, long themePtr, + @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve); + private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag, + String prefix); + static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr); + + // AssetInputStream related native methods. + private static native void nativeAssetDestroy(long assetPtr); + private static native int nativeAssetReadChar(long assetPtr); + private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len); + private static native long nativeAssetSeek(long assetPtr, long offset, int whence); + private static native long nativeAssetGetLength(long assetPtr); + private static native long nativeAssetGetRemainingLength(long assetPtr); + + private static native void nativeVerifySystemIdmaps(); + + // Global debug native methods. + /** + * @hide + */ + public static native int getGlobalAssetCount(); + + /** + * @hide + */ + public static native String getAssetAllocations(); + + /** + * @hide + */ + public static native int getGlobalAssetManagerCount(); } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index ad85e71b86f9..d8133824f757 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -590,7 +590,7 @@ public class Resources { */ @NonNull public int[] getIntArray(@ArrayRes int id) throws NotFoundException { - int[] res = mResourcesImpl.getAssets().getArrayIntResource(id); + int[] res = mResourcesImpl.getAssets().getResourceIntArray(id); if (res != null) { return res; } @@ -613,13 +613,13 @@ public class Resources { @NonNull public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException { final ResourcesImpl impl = mResourcesImpl; - int len = impl.getAssets().getArraySize(id); + int len = impl.getAssets().getResourceArraySize(id); if (len < 0) { throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id)); } TypedArray array = TypedArray.obtain(this, len); - array.mLength = impl.getAssets().retrieveArray(id, array.mData); + array.mLength = impl.getAssets().getResourceArray(id, array.mData); array.mIndices[0] = 0; return array; @@ -1794,8 +1794,7 @@ public class Resources { // out the attributes from the XML file (applying type information // contained in the resources and such). XmlBlock.Parser parser = (XmlBlock.Parser)set; - mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, attrs, - array.mData, array.mIndices); + mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices); array.mXml = parser; diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 424fa833cd48..157910a043e9 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -170,7 +170,6 @@ public class ResourcesImpl { mDisplayAdjustments = displayAdjustments; mConfiguration.setToDefaults(); updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo()); - mAssets.ensureStringBlocks(); } public DisplayAdjustments getDisplayAdjustments() { @@ -1300,8 +1299,7 @@ public class ResourcesImpl { void applyStyle(int resId, boolean force) { synchronized (mKey) { - AssetManager.applyThemeStyle(mTheme, resId, force); - + mAssets.applyStyleToTheme(mTheme, resId, force); mThemeResId = resId; mKey.append(resId, force); } @@ -1310,7 +1308,7 @@ public class ResourcesImpl { void setTo(ThemeImpl other) { synchronized (mKey) { synchronized (other.mKey) { - AssetManager.copyTheme(mTheme, other.mTheme); + AssetManager.nativeThemeCopy(mTheme, other.mTheme); mThemeResId = other.mThemeResId; mKey.setTo(other.getKey()); @@ -1333,12 +1331,10 @@ public class ResourcesImpl { // out the attributes from the XML file (applying type information // contained in the resources and such). final XmlBlock.Parser parser = (XmlBlock.Parser) set; - AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes, - parser != null ? parser.mParseState : 0, - attrs, attrs.length, array.mDataAddress, array.mIndicesAddress); + mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs, + array.mDataAddress, array.mIndicesAddress); array.mTheme = wrapper; array.mXml = parser; - return array; } } @@ -1355,7 +1351,7 @@ public class ResourcesImpl { } final TypedArray array = TypedArray.obtain(wrapper.getResources(), len); - AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); + mAssets.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); array.mTheme = wrapper; array.mXml = null; return array; @@ -1375,14 +1371,14 @@ public class ResourcesImpl { @Config int getChangingConfigurations() { synchronized (mKey) { final @NativeConfig int nativeChangingConfig = - AssetManager.getThemeChangingConfigurations(mTheme); + AssetManager.nativeThemeGetChangingConfigurations(mTheme); return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig); } } public void dump(int priority, String tag, String prefix) { synchronized (mKey) { - AssetManager.dumpTheme(mTheme, priority, tag, prefix); + mAssets.dumpTheme(mTheme, priority, tag, prefix); } } @@ -1411,13 +1407,13 @@ public class ResourcesImpl { */ void rebase() { synchronized (mKey) { - AssetManager.clearTheme(mTheme); + AssetManager.nativeThemeClear(mTheme); // Reapply the same styles in the same order. for (int i = 0; i < mKey.mCount; i++) { final int resId = mKey.mResId[i]; final boolean force = mKey.mForce[i]; - AssetManager.applyThemeStyle(mTheme, resId, force); + mAssets.applyStyleToTheme(mTheme, resId, force); } } } diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index f33c75168a5f..cbb3c6df0558 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -61,6 +61,15 @@ public class TypedArray { return attrs; } + // STYLE_ prefixed constants are offsets within the typed data array. + static final int STYLE_NUM_ENTRIES = 6; + static final int STYLE_TYPE = 0; + static final int STYLE_DATA = 1; + static final int STYLE_ASSET_COOKIE = 2; + static final int STYLE_RESOURCE_ID = 3; + static final int STYLE_CHANGING_CONFIGURATIONS = 4; + static final int STYLE_DENSITY = 5; + private final Resources mResources; private DisplayMetrics mMetrics; private AssetManager mAssets; @@ -78,7 +87,7 @@ public class TypedArray { private void resize(int len) { mLength = len; - final int dataLen = len * AssetManager.STYLE_NUM_ENTRIES; + final int dataLen = len * STYLE_NUM_ENTRIES; final int indicesLen = len + 1; final VMRuntime runtime = VMRuntime.getRuntime(); if (mDataAddress == 0 || mData.length < dataLen) { @@ -166,9 +175,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -203,9 +212,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return null; } else if (type == TypedValue.TYPE_STRING) { @@ -242,14 +251,13 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_STRING) { - final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; + final int cookie = data[index + STYLE_ASSET_COOKIE]; if (cookie < 0) { - return mXml.getPooledString( - data[index+AssetManager.STYLE_DATA]).toString(); + return mXml.getPooledString(data[index + STYLE_DATA]).toString(); } } return null; @@ -274,11 +282,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); + data[index + STYLE_CHANGING_CONFIGURATIONS]); if ((changingConfigs & ~allowedChangingConfigs) != 0) { return null; } @@ -320,14 +328,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA] != 0; + return data[index + STYLE_DATA] != 0; } final TypedValue v = mValue; @@ -359,14 +367,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } final TypedValue v = mValue; @@ -396,16 +404,16 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FLOAT) { - return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]); + return Float.intBitsToFloat(data[index + STYLE_DATA]); } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } final TypedValue v = mValue; @@ -446,15 +454,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_STRING) { final TypedValue value = mValue; if (getValueAt(index, value)) { @@ -498,7 +506,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -533,7 +541,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -564,15 +572,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -612,15 +620,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimension( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -661,15 +668,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelOffset( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -711,15 +717,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index+AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -755,16 +760,15 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index+AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -795,15 +799,14 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type >= TypedValue.TYPE_FIRST_INT && type <= TypedValue.TYPE_LAST_INT) { - return data[index+AssetManager.STYLE_DATA]; + return data[index + STYLE_DATA]; } else if (type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - data[index + AssetManager.STYLE_DATA], mMetrics); + return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics); } return defValue; @@ -833,15 +836,14 @@ public class TypedArray { } final int attrIndex = index; - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return defValue; } else if (type == TypedValue.TYPE_FRACTION) { - return TypedValue.complexToFraction( - data[index+AssetManager.STYLE_DATA], base, pbase); + return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase); } else if (type == TypedValue.TYPE_ATTRIBUTE) { final TypedValue value = mValue; getValueAt(index, value); @@ -874,10 +876,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) { - final int resid = data[index+AssetManager.STYLE_RESOURCE_ID]; + if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) { + final int resid = data[index + STYLE_RESOURCE_ID]; if (resid != 0) { return resid; } @@ -902,10 +904,10 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { - return data[index + AssetManager.STYLE_DATA]; + if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { + return data[index + STYLE_DATA]; } return defValue; } @@ -939,7 +941,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -975,7 +977,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { if (value.type == TypedValue.TYPE_ATTRIBUTE) { throw new UnsupportedOperationException( "Failed to resolve attribute at index " + index + ": " + value); @@ -1006,7 +1008,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { return mResources.getTextArray(value.resourceId); } return null; @@ -1027,7 +1029,7 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue); + return getValueAt(index * STYLE_NUM_ENTRIES, outValue); } /** @@ -1043,8 +1045,8 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; - return mData[index + AssetManager.STYLE_TYPE]; + index *= STYLE_NUM_ENTRIES; + return mData[index + STYLE_TYPE]; } /** @@ -1063,9 +1065,9 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; return type != TypedValue.TYPE_NULL; } @@ -1084,11 +1086,11 @@ public class TypedArray { throw new RuntimeException("Cannot make calls to a recycled instance!"); } - index *= AssetManager.STYLE_NUM_ENTRIES; + index *= STYLE_NUM_ENTRIES; final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; return type != TypedValue.TYPE_NULL - || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; + || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; } /** @@ -1109,7 +1111,7 @@ public class TypedArray { } final TypedValue value = mValue; - if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { + if (getValueAt(index * STYLE_NUM_ENTRIES, value)) { return value; } return null; @@ -1181,16 +1183,16 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * AssetManager.STYLE_NUM_ENTRIES; - if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { + final int index = i * STYLE_NUM_ENTRIES; + if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { // Not an attribute, ignore. continue; } // Null the entry so that we can safely call getZzz(). - data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL; + data[index + STYLE_TYPE] = TypedValue.TYPE_NULL; - final int attr = data[index + AssetManager.STYLE_DATA]; + final int attr = data[index + STYLE_DATA]; if (attr == 0) { // Useless data, ignore. continue; @@ -1231,45 +1233,44 @@ public class TypedArray { final int[] data = mData; final int N = length(); for (int i = 0; i < N; i++) { - final int index = i * AssetManager.STYLE_NUM_ENTRIES; - final int type = data[index + AssetManager.STYLE_TYPE]; + final int index = i * STYLE_NUM_ENTRIES; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { continue; } changingConfig |= ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); + data[index + STYLE_CHANGING_CONFIGURATIONS]); } return changingConfig; } private boolean getValueAt(int index, TypedValue outValue) { final int[] data = mData; - final int type = data[index+AssetManager.STYLE_TYPE]; + final int type = data[index + STYLE_TYPE]; if (type == TypedValue.TYPE_NULL) { return false; } outValue.type = type; - outValue.data = data[index+AssetManager.STYLE_DATA]; - outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; - outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID]; + outValue.data = data[index + STYLE_DATA]; + outValue.assetCookie = data[index + STYLE_ASSET_COOKIE]; + outValue.resourceId = data[index + STYLE_RESOURCE_ID]; outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( - data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]); - outValue.density = data[index+AssetManager.STYLE_DENSITY]; + data[index + STYLE_CHANGING_CONFIGURATIONS]); + outValue.density = data[index + STYLE_DENSITY]; outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null; return true; } private CharSequence loadStringValueAt(int index) { final int[] data = mData; - final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; + final int cookie = data[index + STYLE_ASSET_COOKIE]; if (cookie < 0) { if (mXml != null) { - return mXml.getPooledString( - data[index+AssetManager.STYLE_DATA]); + return mXml.getPooledString(data[index + STYLE_DATA]); } return null; } - return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]); + return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]); } /** @hide */ diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java index e6b957414ea8..d4ccffb83ca5 100644 --- a/core/java/android/content/res/XmlBlock.java +++ b/core/java/android/content/res/XmlBlock.java @@ -16,6 +16,7 @@ package android.content.res; +import android.annotation.Nullable; import android.util.TypedValue; import com.android.internal.util.XmlUtils; @@ -33,7 +34,7 @@ import java.io.Reader; * * {@hide} */ -final class XmlBlock { +final class XmlBlock implements AutoCloseable { private static final boolean DEBUG=false; public XmlBlock(byte[] data) { @@ -48,6 +49,7 @@ final class XmlBlock { mStrings = new StringBlock(nativeGetStringBlock(mNative), false); } + @Override public void close() { synchronized (this) { if (mOpen) { @@ -478,13 +480,13 @@ final class XmlBlock { * are doing! The given native object must exist for the entire lifetime * of this newly creating XmlBlock. */ - XmlBlock(AssetManager assets, long xmlBlock) { + XmlBlock(@Nullable AssetManager assets, long xmlBlock) { mAssets = assets; mNative = xmlBlock; mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false); } - private final AssetManager mAssets; + private @Nullable final AssetManager mAssets; private final long mNative; /*package*/ final StringBlock mStrings; private boolean mOpen = true; diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index f08e1cc24b26..9a276fbd1ec0 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -3535,8 +3535,8 @@ public class Camera { /** * Gets the focal length (in millimeter) of the camera. * - * @return the focal length. This method will always return a valid - * value. + * @return the focal length. Returns -1.0 when the device + * doesn't report focal length information. */ public float getFocalLength() { return Float.parseFloat(get(KEY_FOCAL_LENGTH)); diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 17b46c68ebe1..ef58f94e70cf 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.IntDef; +import android.annotation.SystemApi; import android.net.ConnectivityManager.NetworkCallback; import android.os.Parcel; import android.os.Parcelable; @@ -69,6 +70,7 @@ public final class NetworkCapabilities implements Parcelable { mSignalStrength = nc.mSignalStrength; mUids = nc.mUids; mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid; + mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities; } } @@ -78,7 +80,7 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ public void clearAll() { - mNetworkCapabilities = mTransportTypes = 0; + mNetworkCapabilities = mTransportTypes = mUnwantedNetworkCapabilities = 0; mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED; mNetworkSpecifier = null; mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED; @@ -92,6 +94,11 @@ public final class NetworkCapabilities implements Parcelable { */ private long mNetworkCapabilities; + /** + * If any capabilities specified here they must not exist in the matching Network. + */ + private long mUnwantedNetworkCapabilities; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "NET_CAPABILITY_" }, value = { @@ -339,31 +346,55 @@ public final class NetworkCapabilities implements Parcelable { * Adds the given capability to this {@code NetworkCapability} instance. * Multiple capabilities may be applied sequentially. Note that when searching * for a network to satisfy a request, all capabilities requested must be satisfied. + * <p> + * If the given capability was previously added to the list of unwanted capabilities + * then the capability will also be removed from the list of unwanted capabilities. * * @param capability the capability to be added. * @return This NetworkCapabilities instance, to facilitate chaining. * @hide */ public NetworkCapabilities addCapability(@NetCapability int capability) { - if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) { - throw new IllegalArgumentException("NetworkCapability out of range"); - } + checkValidCapability(capability); mNetworkCapabilities |= 1 << capability; + mUnwantedNetworkCapabilities &= ~(1 << capability); // remove from unwanted capability list return this; } /** + * Adds the given capability to the list of unwanted capabilities of this + * {@code NetworkCapability} instance. Multiple unwanted capabilities may be applied + * sequentially. Note that when searching for a network to satisfy a request, the network + * must not contain any capability from unwanted capability list. + * <p> + * If the capability was previously added to the list of required capabilities (for + * example, it was there by default or added using {@link #addCapability(int)} method), then + * it will be removed from the list of required capabilities as well. + * + * @see #addCapability(int) + * @hide + */ + public void addUnwantedCapability(@NetCapability int capability) { + checkValidCapability(capability); + mUnwantedNetworkCapabilities |= 1 << capability; + mNetworkCapabilities &= ~(1 << capability); // remove from requested capabilities + } + + /** * Removes (if found) the given capability from this {@code NetworkCapability} instance. + * <p> + * Note that this method removes capabilities that was added via {@link #addCapability(int)}, + * {@link #addUnwantedCapability(int)} or {@link #setCapabilities(int[], int[])} . * * @param capability the capability to be removed. * @return This NetworkCapabilities instance, to facilitate chaining. * @hide */ public NetworkCapabilities removeCapability(@NetCapability int capability) { - if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) { - throw new IllegalArgumentException("NetworkCapability out of range"); - } - mNetworkCapabilities &= ~(1 << capability); + checkValidCapability(capability); + final long mask = ~(1 << capability); + mNetworkCapabilities &= mask; + mUnwantedNetworkCapabilities &= mask; return this; } @@ -393,30 +424,57 @@ public final class NetworkCapabilities implements Parcelable { } /** + * Gets all the unwanted capabilities set on this {@code NetworkCapability} instance. + * + * @return an array of unwanted capability values for this instance. + * @hide + */ + public @NetCapability int[] getUnwantedCapabilities() { + return BitUtils.unpackBits(mUnwantedNetworkCapabilities); + } + + + /** * Sets all the capabilities set on this {@code NetworkCapability} instance. * This overwrites any existing capabilities. * * @hide */ - public void setCapabilities(@NetCapability int[] capabilities) { + public void setCapabilities(@NetCapability int[] capabilities, + @NetCapability int[] unwantedCapabilities) { mNetworkCapabilities = BitUtils.packBits(capabilities); + mUnwantedNetworkCapabilities = BitUtils.packBits(unwantedCapabilities); + } + + /** + * @deprecated use {@link #setCapabilities(int[], int[])} + * @hide + */ + @Deprecated + public void setCapabilities(@NetCapability int[] capabilities) { + setCapabilities(capabilities, new int[] {}); } /** - * Tests for the presence of a capabilitity on this instance. + * Tests for the presence of a capability on this instance. * * @param capability the capabilities to be tested for. * @return {@code true} if set on this instance. */ public boolean hasCapability(@NetCapability int capability) { - if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) { - return false; - } - return ((mNetworkCapabilities & (1 << capability)) != 0); + return isValidCapability(capability) + && ((mNetworkCapabilities & (1 << capability)) != 0); + } + + /** @hide */ + public boolean hasUnwantedCapability(@NetCapability int capability) { + return isValidCapability(capability) + && ((mUnwantedNetworkCapabilities & (1 << capability)) != 0); } private void combineNetCapabilities(NetworkCapabilities nc) { this.mNetworkCapabilities |= nc.mNetworkCapabilities; + this.mUnwantedNetworkCapabilities |= nc.mUnwantedNetworkCapabilities; } /** @@ -427,7 +485,9 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ public String describeFirstNonRequestableCapability() { - final long nonRequestable = (mNetworkCapabilities & NON_REQUESTABLE_CAPABILITIES); + final long nonRequestable = (mNetworkCapabilities | mUnwantedNetworkCapabilities) + & NON_REQUESTABLE_CAPABILITIES; + if (nonRequestable != 0) { return capabilityNameOf(BitUtils.unpackBits(nonRequestable)[0]); } @@ -437,21 +497,29 @@ public final class NetworkCapabilities implements Parcelable { } private boolean satisfiedByNetCapabilities(NetworkCapabilities nc, boolean onlyImmutable) { - long networkCapabilities = this.mNetworkCapabilities; + long requestedCapabilities = mNetworkCapabilities; + long requestedUnwantedCapabilities = mUnwantedNetworkCapabilities; + long providedCapabilities = nc.mNetworkCapabilities; + if (onlyImmutable) { - networkCapabilities = networkCapabilities & ~MUTABLE_CAPABILITIES; + requestedCapabilities &= ~MUTABLE_CAPABILITIES; + requestedUnwantedCapabilities &= ~MUTABLE_CAPABILITIES; } - return ((nc.mNetworkCapabilities & networkCapabilities) == networkCapabilities); + return ((providedCapabilities & requestedCapabilities) == requestedCapabilities) + && ((requestedUnwantedCapabilities & providedCapabilities) == 0); } /** @hide */ public boolean equalsNetCapabilities(NetworkCapabilities nc) { - return (nc.mNetworkCapabilities == this.mNetworkCapabilities); + return (nc.mNetworkCapabilities == this.mNetworkCapabilities) + && (nc.mUnwantedNetworkCapabilities == this.mUnwantedNetworkCapabilities); } private boolean equalsNetCapabilitiesRequestable(NetworkCapabilities that) { return ((this.mNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES) == - (that.mNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES)); + (that.mNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES)) + && ((this.mUnwantedNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES) == + (that.mUnwantedNetworkCapabilities & ~NON_REQUESTABLE_CAPABILITIES)); } /** @@ -1185,15 +1253,17 @@ public final class NetworkCapabilities implements Parcelable { @Override public int hashCode() { - return ((int) (mNetworkCapabilities & 0xFFFFFFFF) + return (int) (mNetworkCapabilities & 0xFFFFFFFF) + ((int) (mNetworkCapabilities >> 32) * 3) - + ((int) (mTransportTypes & 0xFFFFFFFF) * 5) - + ((int) (mTransportTypes >> 32) * 7) - + (mLinkUpBandwidthKbps * 11) - + (mLinkDownBandwidthKbps * 13) - + Objects.hashCode(mNetworkSpecifier) * 17 - + (mSignalStrength * 19) - + Objects.hashCode(mUids) * 23); + + ((int) (mUnwantedNetworkCapabilities & 0xFFFFFFFF) * 5) + + ((int) (mUnwantedNetworkCapabilities >> 32) * 7) + + ((int) (mTransportTypes & 0xFFFFFFFF) * 11) + + ((int) (mTransportTypes >> 32) * 13) + + (mLinkUpBandwidthKbps * 17) + + (mLinkDownBandwidthKbps * 19) + + Objects.hashCode(mNetworkSpecifier) * 23 + + (mSignalStrength * 29) + + Objects.hashCode(mUids) * 31; } @Override @@ -1203,6 +1273,7 @@ public final class NetworkCapabilities implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeLong(mNetworkCapabilities); + dest.writeLong(mUnwantedNetworkCapabilities); dest.writeLong(mTransportTypes); dest.writeInt(mLinkUpBandwidthKbps); dest.writeInt(mLinkDownBandwidthKbps); @@ -1218,6 +1289,7 @@ public final class NetworkCapabilities implements Parcelable { NetworkCapabilities netCap = new NetworkCapabilities(); netCap.mNetworkCapabilities = in.readLong(); + netCap.mUnwantedNetworkCapabilities = in.readLong(); netCap.mTransportTypes = in.readLong(); netCap.mLinkUpBandwidthKbps = in.readInt(); netCap.mLinkDownBandwidthKbps = in.readInt(); @@ -1246,6 +1318,11 @@ public final class NetworkCapabilities implements Parcelable { appendStringRepresentationOfBitMaskToStringBuilder(sb, mNetworkCapabilities, NetworkCapabilities::capabilityNameOf, "&"); } + if (0 != mNetworkCapabilities) { + sb.append(" Unwanted: "); + appendStringRepresentationOfBitMaskToStringBuilder(sb, mUnwantedNetworkCapabilities, + NetworkCapabilities::capabilityNameOf, "&"); + } if (mLinkUpBandwidthKbps > 0) { sb.append(" LinkUpBandwidth>=").append(mLinkUpBandwidthKbps).append("Kbps"); } @@ -1396,4 +1473,13 @@ public final class NetworkCapabilities implements Parcelable { Preconditions.checkArgument( isValidTransport(transport), "Invalid TransportType " + transport); } + + private static boolean isValidCapability(@NetworkCapabilities.NetCapability int capability) { + return capability >= MIN_NET_CAPABILITY && capability <= MAX_NET_CAPABILITY; + } + + private static void checkValidCapability(@NetworkCapabilities.NetCapability int capability) { + Preconditions.checkArgument(isValidCapability(capability), + "NetworkCapability " + capability + "out of range"); + } } diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 61199f906e4a..fdcc304e1d2e 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -165,6 +165,9 @@ public class NetworkRequest implements Parcelable { * the requested network's required capabilities. Note that when searching * for a network to satisfy a request, all capabilities requested must be * satisfied. + * <p> + * If the given capability was previously added to the list of unwanted capabilities + * then the capability will also be removed from the list of unwanted capabilities. * * @param capability The capability to add. * @return The builder to facilitate chaining @@ -176,7 +179,8 @@ public class NetworkRequest implements Parcelable { } /** - * Removes (if found) the given capability from this builder instance. + * Removes (if found) the given capability from this builder instance from both required + * and unwanted capabilities lists. * * @param capability The capability to remove. * @return The builder to facilitate chaining. @@ -201,6 +205,24 @@ public class NetworkRequest implements Parcelable { } /** + * Add a capability that must not exist in the requested network. + * <p> + * If the capability was previously added to the list of required capabilities (for + * example, it was there by default or added using {@link #addCapability(int)} method), then + * it will be removed from the list of required capabilities as well. + * + * @see #addCapability(int) + * + * @param capability The capability to add to unwanted capability list. + * @return The builder to facilitate chaining. + * @hide + */ + public Builder addUnwantedCapability(@NetworkCapabilities.NetCapability int capability) { + mNetworkCapabilities.addUnwantedCapability(capability); + return this; + } + + /** * Completely clears all the {@code NetworkCapabilities} from this builder instance, * removing even the capabilities that are set by default when the object is constructed. * diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 5e9d39ce6af0..7bc5d5bd33bf 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -294,8 +294,8 @@ public abstract class BatteryStats implements Parcelable { /** * jbd line is: * BATTERY_STATS_CHECKIN_VERSION, uid, which, "jbd", - * jobsDeferredEventCount, jobsDeferredCount, averageLatencyMillis, - * count at latency < 1 hr, count at latency < 2 hrs, count at latency < 6 hrs, beyond 6 hrs + * jobsDeferredEventCount, jobsDeferredCount, totalLatencyMillis, + * count at latency < 1 hr, count at latency 1 to 2 hrs, 2 to 4 hrs, 4 to 8 hrs, and past 8 hrs * <p> * @see #JOB_FRESHNESS_BUCKETS */ @@ -364,13 +364,14 @@ public abstract class BatteryStats implements Parcelable { /** * These are the thresholds for bucketing last time since a job was run for an app * that just moved to ACTIVE due to a launch. So if the last time a job ran was less - * than 30 minutes ago, then it's reasonably fresh, 2 hours ago, not so fresh and so + * than 1 hour ago, then it's reasonably fresh, 2 hours ago, not so fresh and so * on. */ public static final long[] JOB_FRESHNESS_BUCKETS = { 1 * 60 * 60 * 1000L, 2 * 60 * 60 * 1000L, - 6 * 60 * 60 * 1000L, + 4 * 60 * 60 * 1000L, + 8 * 60 * 60 * 1000L, Long.MAX_VALUE }; @@ -2269,27 +2270,27 @@ public abstract class BatteryStats implements Parcelable { */ public abstract int getMobileRadioActiveUnknownCount(int which); - public static final int DATA_CONNECTION_NONE = 0; - public static final int DATA_CONNECTION_GPRS = 1; - public static final int DATA_CONNECTION_EDGE = 2; - public static final int DATA_CONNECTION_UMTS = 3; - public static final int DATA_CONNECTION_CDMA = 4; - public static final int DATA_CONNECTION_EVDO_0 = 5; - public static final int DATA_CONNECTION_EVDO_A = 6; - public static final int DATA_CONNECTION_1xRTT = 7; - public static final int DATA_CONNECTION_HSDPA = 8; - public static final int DATA_CONNECTION_HSUPA = 9; - public static final int DATA_CONNECTION_HSPA = 10; - public static final int DATA_CONNECTION_IDEN = 11; - public static final int DATA_CONNECTION_EVDO_B = 12; - public static final int DATA_CONNECTION_LTE = 13; - public static final int DATA_CONNECTION_EHRPD = 14; - public static final int DATA_CONNECTION_HSPAP = 15; - public static final int DATA_CONNECTION_GSM = 16; - public static final int DATA_CONNECTION_TD_SCDMA = 17; - public static final int DATA_CONNECTION_IWLAN = 18; - public static final int DATA_CONNECTION_LTE_CA = 19; - public static final int DATA_CONNECTION_OTHER = 20; + public static final int DATA_CONNECTION_NONE = SystemProto.DataConnection.NONE; // 0 + public static final int DATA_CONNECTION_GPRS = SystemProto.DataConnection.GPRS; // 1 + public static final int DATA_CONNECTION_EDGE = SystemProto.DataConnection.EDGE; // 2 + public static final int DATA_CONNECTION_UMTS = SystemProto.DataConnection.UMTS; // 3 + public static final int DATA_CONNECTION_CDMA = SystemProto.DataConnection.CDMA; // 4 + public static final int DATA_CONNECTION_EVDO_0 = SystemProto.DataConnection.EVDO_0; // 5 + public static final int DATA_CONNECTION_EVDO_A = SystemProto.DataConnection.EVDO_A; // 6 + public static final int DATA_CONNECTION_1xRTT = SystemProto.DataConnection.ONE_X_RTT; // 7 + public static final int DATA_CONNECTION_HSDPA = SystemProto.DataConnection.HSDPA; // 8 + public static final int DATA_CONNECTION_HSUPA = SystemProto.DataConnection.HSUPA; // 9 + public static final int DATA_CONNECTION_HSPA = SystemProto.DataConnection.HSPA; // 10 + public static final int DATA_CONNECTION_IDEN = SystemProto.DataConnection.IDEN; // 11 + public static final int DATA_CONNECTION_EVDO_B = SystemProto.DataConnection.EVDO_B; // 12 + public static final int DATA_CONNECTION_LTE = SystemProto.DataConnection.LTE; // 13 + public static final int DATA_CONNECTION_EHRPD = SystemProto.DataConnection.EHRPD; // 14 + public static final int DATA_CONNECTION_HSPAP = SystemProto.DataConnection.HSPAP; // 15 + public static final int DATA_CONNECTION_GSM = SystemProto.DataConnection.GSM; // 16 + public static final int DATA_CONNECTION_TD_SCDMA = SystemProto.DataConnection.TD_SCDMA; // 17 + public static final int DATA_CONNECTION_IWLAN = SystemProto.DataConnection.IWLAN; // 18 + public static final int DATA_CONNECTION_LTE_CA = SystemProto.DataConnection.LTE_CA; // 19 + public static final int DATA_CONNECTION_OTHER = SystemProto.DataConnection.OTHER; // 20 static final String[] DATA_CONNECTION_NAMES = { "none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A", diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8fc690144aee..6fe249a2bfb0 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -84,6 +84,7 @@ import android.util.ArraySet; import android.util.Log; import android.util.MemoryIntArray; import android.util.StatsLog; +import android.view.textservice.TextServicesManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.widget.ILockSettings; @@ -7970,6 +7971,10 @@ public final class Settings { CLONE_TO_MANAGED_PROFILE.add(LOCATION_MODE); CLONE_TO_MANAGED_PROFILE.add(LOCATION_PROVIDERS_ALLOWED); CLONE_TO_MANAGED_PROFILE.add(SELECTED_INPUT_METHOD_SUBTYPE); + if (TextServicesManager.DISABLE_PER_PROFILE_SPELL_CHECKER) { + CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER); + CLONE_TO_MANAGED_PROFILE.add(SELECTED_SPELL_CHECKER_SUBTYPE); + } } /** @hide */ diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java index 674f809ef0f5..70dfef574ca5 100644 --- a/core/java/android/speech/RecognitionService.java +++ b/core/java/android/speech/RecognitionService.java @@ -20,7 +20,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.app.Service; import android.content.Intent; -import android.content.pm.PackageManager; +import android.content.PermissionChecker; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -174,8 +174,8 @@ public abstract class RecognitionService extends Service { */ private boolean checkPermissions(IRecognitionListener listener) { if (DBG) Log.d(TAG, "checkPermissions"); - if (RecognitionService.this.checkCallingOrSelfPermission(android.Manifest.permission. - RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) { + if (PermissionChecker.checkCallingOrSelfPermission(this, + android.Manifest.permission.RECORD_AUDIO) == PermissionChecker.PERMISSION_GRANTED) { return true; } try { diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 914ba0c673aa..75940e8b6129 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -283,6 +283,11 @@ interface IWindowManager */ oneway void setPipVisibility(boolean visible); + /** + * Called by System UI to enable or disable haptic feedback on the navigation bar buttons. + */ + void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled); + /** * Device has a software navigation bar (separate from the status bar). */ diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index db19681840d7..e996ea160126 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -20,11 +20,13 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.app.ActivityManager; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.AnimatedVectorDrawable; +import android.os.Build; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -1066,6 +1068,9 @@ public final class ThreadedRenderer { mInitialized = true; mAppContext = context.getApplicationContext(); + nSetDebuggingEnabled( + (mAppContext.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0 + || Build.IS_DEBUGGABLE); initSched(renderProxy); initGraphicsStats(); } @@ -1194,4 +1199,5 @@ public final class ThreadedRenderer { private static native void nSetHighContrastText(boolean enabled); // For temporary experimentation b/66945974 private static native void nHackySetRTAnimationsEnabled(boolean enabled); + private static native void nSetDebuggingEnabled(boolean enabled); } diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index c5a94daaba53..7a9de45cbbf8 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -303,6 +303,7 @@ public class ViewConfiguration { private final long mGlobalActionsKeyTimeout; private final float mVerticalScrollFactor; private final float mHorizontalScrollFactor; + private final boolean mShowMenuShortcutsWhenKeyboardPresent; private boolean sHasPermanentMenuKey; private boolean sHasPermanentMenuKeySet; @@ -335,6 +336,7 @@ public class ViewConfiguration { mGlobalActionsKeyTimeout = GLOBAL_ACTIONS_KEY_TIMEOUT; mHorizontalScrollFactor = HORIZONTAL_SCROLL_FACTOR; mVerticalScrollFactor = VERTICAL_SCROLL_FACTOR; + mShowMenuShortcutsWhenKeyboardPresent = false; } /** @@ -428,6 +430,10 @@ public class ViewConfiguration { com.android.internal.R.dimen.config_horizontalScrollFactor); mVerticalScrollFactor = res.getDimensionPixelSize( com.android.internal.R.dimen.config_verticalScrollFactor); + + mShowMenuShortcutsWhenKeyboardPresent = res.getBoolean( + com.android.internal.R.bool.config_showMenuShortcutsWhenKeyboardPresent); + } /** @@ -910,6 +916,15 @@ public class ViewConfiguration { } /** + * Check if shortcuts should be displayed in menus. + * + * @return {@code True} if shortcuts should be displayed in menus. + */ + public boolean shouldShowMenuShortcutsWhenKeyboardPresent() { + return mShowMenuShortcutsWhenKeyboardPresent; + } + + /** * @hide * @return Whether or not marquee should use fading edges. */ diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java index f368c74a17b5..21ec42b1d557 100644 --- a/core/java/android/view/textservice/TextServicesManager.java +++ b/core/java/android/view/textservice/TextServicesManager.java @@ -66,6 +66,12 @@ public final class TextServicesManager { private static final String TAG = TextServicesManager.class.getSimpleName(); private static final boolean DBG = false; + /** + * A compile time switch to control per-profile spell checker, which is not yet ready. + * @hide + */ + public static final boolean DISABLE_PER_PROFILE_SPELL_CHECKER = true; + private static TextServicesManager sInstance; private final ITextServicesManager mService; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 50e6393d2579..b482d4715d48 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -2025,7 +2025,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final boolean italic = (style & Typeface.ITALIC) != 0; setTypeface(Typeface.create(typeface, weight, italic)); } else { - setTypeface(Typeface.create(typeface, style)); + setTypeface(typeface, style); } } @@ -2111,7 +2111,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_typeface * @attr ref android.R.styleable#TextView_textStyle */ - public void setTypeface(Typeface tf, int style) { + public void setTypeface(@Nullable Typeface tf, @Typeface.Style int style) { if (style > 0) { if (tf == null) { tf = Typeface.defaultFromStyle(style); @@ -3896,7 +3896,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_typeface * @attr ref android.R.styleable#TextView_textStyle */ - public void setTypeface(Typeface tf) { + public void setTypeface(@Nullable Typeface tf) { if (mTextPaint.getTypeface() != tf) { mTextPaint.setTypeface(tf); diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index edcf209bdf81..d74a60e483e5 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -531,6 +531,14 @@ public class Toast { mWM.removeViewImmediate(mView); } + + // Now that we've removed the view it's safe for the server to release + // the resources. + try { + getService().finishToken(mPackageName, this); + } catch (RemoteException e) { + } + mView = null; } } diff --git a/core/java/com/android/internal/globalactions/Action.java b/core/java/com/android/internal/globalactions/Action.java new file mode 100644 index 000000000000..ddb75c1cb11b --- /dev/null +++ b/core/java/com/android/internal/globalactions/Action.java @@ -0,0 +1,42 @@ +/* + * 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.internal.globalactions; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +/** What each item in the global actions dialog must be able to support. */ +public interface Action { + /** @return Text that will be announced when dialog is created. {@code null} for none. */ + CharSequence getLabelForAccessibility(Context context); + + /** Create the view that represents this action. */ + View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater); + + /** Called when the action is selected by the user. */ + void onPress(); + + /** @return whether this action should appear in the dialog when the keygaurd is showing. */ + boolean showDuringKeyguard(); + + /** @return whether this action should appear in the dialog before the device is provisioned. */ + boolean showBeforeProvisioning(); + + /** @return {@code true} if the action is enabled for user interaction. */ + boolean isEnabled(); +} diff --git a/core/java/com/android/internal/globalactions/ActionsAdapter.java b/core/java/com/android/internal/globalactions/ActionsAdapter.java new file mode 100644 index 000000000000..c9f01ce9c08b --- /dev/null +++ b/core/java/com/android/internal/globalactions/ActionsAdapter.java @@ -0,0 +1,112 @@ +/* + * 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.internal.globalactions; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import java.util.function.BooleanSupplier; +import java.util.List; + +/** + * The adapter used for the list within the global actions dialog, taking into account whether the + * keyguard is showing via {@link LegacyGlobalActions#mKeyguardShowing} and whether the device is + * provisioned via {@link LegacyGlobalActions#mDeviceProvisioned}. + */ +public class ActionsAdapter extends BaseAdapter { + private final Context mContext; + private final List<Action> mItems; + private final BooleanSupplier mDeviceProvisioned; + private final BooleanSupplier mKeyguardShowing; + + public ActionsAdapter(Context context, List<Action> items, + BooleanSupplier deviceProvisioned, BooleanSupplier keyguardShowing) { + mContext = context; + mItems = items; + mDeviceProvisioned = deviceProvisioned; + mKeyguardShowing = keyguardShowing; + } + + @Override + public int getCount() { + final boolean keyguardShowing = mKeyguardShowing.getAsBoolean(); + final boolean deviceProvisioned = mDeviceProvisioned.getAsBoolean(); + int count = 0; + + for (int i = 0; i < mItems.size(); i++) { + final Action action = mItems.get(i); + + if (keyguardShowing && !action.showDuringKeyguard()) { + continue; + } + if (!deviceProvisioned && !action.showBeforeProvisioning()) { + continue; + } + count++; + } + return count; + } + + @Override + public boolean isEnabled(int position) { + return getItem(position).isEnabled(); + } + + @Override + public boolean areAllItemsEnabled() { + return false; + } + + @Override + public Action getItem(int position) { + final boolean keyguardShowing = mKeyguardShowing.getAsBoolean(); + final boolean deviceProvisioned = mDeviceProvisioned.getAsBoolean(); + + int filteredPos = 0; + for (int i = 0; i < mItems.size(); i++) { + final Action action = mItems.get(i); + if (keyguardShowing && !action.showDuringKeyguard()) { + continue; + } + if (!deviceProvisioned && !action.showBeforeProvisioning()) { + continue; + } + if (filteredPos == position) { + return action; + } + filteredPos++; + } + + throw new IllegalArgumentException("position " + position + + " out of range of showable actions" + + ", filtered count=" + getCount() + + ", keyguardshowing=" + keyguardShowing + + ", provisioned=" + deviceProvisioned); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + Action action = getItem(position); + return action.create(mContext, convertView, parent, LayoutInflater.from(mContext)); + } +} diff --git a/core/java/com/android/internal/globalactions/ActionsDialog.java b/core/java/com/android/internal/globalactions/ActionsDialog.java new file mode 100644 index 000000000000..1cca1cc64ca1 --- /dev/null +++ b/core/java/com/android/internal/globalactions/ActionsDialog.java @@ -0,0 +1,94 @@ +/* + * 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.internal.globalactions; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.util.TypedValue; +import android.view.accessibility.AccessibilityEvent; +import android.view.KeyEvent; +import android.widget.ListView; +import com.android.internal.app.AlertController; + +/** A dialog that lists the given Action items to be user selectable. */ +public final class ActionsDialog extends Dialog implements DialogInterface { + private final Context mContext; + private final AlertController mAlert; + private final ActionsAdapter mAdapter; + + public ActionsDialog(Context context, AlertController.AlertParams params) { + super(context, getDialogTheme(context)); + mContext = getContext(); + mAlert = AlertController.create(mContext, this, getWindow()); + mAdapter = (ActionsAdapter) params.mAdapter; + params.apply(mAlert); + } + + private static int getDialogTheme(Context context) { + TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogTheme, + outValue, true); + return outValue.resourceId; + } + + @Override + protected void onStart() { + super.setCanceledOnTouchOutside(true); + super.onStart(); + } + + public ListView getListView() { + return mAlert.getListView(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mAlert.installContent(); + } + + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { + for (int i = 0; i < mAdapter.getCount(); ++i) { + CharSequence label = + mAdapter.getItem(i).getLabelForAccessibility(getContext()); + if (label != null) { + event.getText().add(label); + } + } + } + return super.dispatchPopulateAccessibilityEvent(event); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (mAlert.onKeyDown(keyCode, event)) { + return true; + } + return super.onKeyDown(keyCode, event); + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + if (mAlert.onKeyUp(keyCode, event)) { + return true; + } + return super.onKeyUp(keyCode, event); + } +} diff --git a/core/java/com/android/internal/globalactions/LongPressAction.java b/core/java/com/android/internal/globalactions/LongPressAction.java new file mode 100644 index 000000000000..eed4cd9fe9b3 --- /dev/null +++ b/core/java/com/android/internal/globalactions/LongPressAction.java @@ -0,0 +1,21 @@ +/* + * 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.internal.globalactions; + +/** An action that also supports long press. */ +public interface LongPressAction extends Action { + boolean onLongPress(); +} diff --git a/core/java/com/android/internal/globalactions/SinglePressAction.java b/core/java/com/android/internal/globalactions/SinglePressAction.java new file mode 100644 index 000000000000..0b8cd0b2c266 --- /dev/null +++ b/core/java/com/android/internal/globalactions/SinglePressAction.java @@ -0,0 +1,99 @@ +/* + * 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.internal.globalactions; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import com.android.internal.R; + +/** A single press action maintains no state, just responds to a press and takes an action. */ +public abstract class SinglePressAction implements Action { + private final int mIconResId; + private final Drawable mIcon; + private final int mMessageResId; + private final CharSequence mMessage; + + protected SinglePressAction(int iconResId, int messageResId) { + mIconResId = iconResId; + mMessageResId = messageResId; + mMessage = null; + mIcon = null; + } + + protected SinglePressAction(int iconResId, Drawable icon, CharSequence message) { + mIconResId = iconResId; + mMessageResId = 0; + mMessage = message; + mIcon = icon; + } + + @Override + public boolean isEnabled() { + return true; + } + + public String getStatus() { + return null; + } + + @Override + abstract public void onPress(); + + @Override + public CharSequence getLabelForAccessibility(Context context) { + if (mMessage != null) { + return mMessage; + } else { + return context.getString(mMessageResId); + } + } + + @Override + public View create( + Context context, View convertView, ViewGroup parent, LayoutInflater inflater) { + View v = inflater.inflate(R.layout.global_actions_item, parent, false); + + ImageView icon = v.findViewById(R.id.icon); + TextView messageView = v.findViewById(R.id.message); + + TextView statusView = v.findViewById(R.id.status); + final String status = getStatus(); + if (!TextUtils.isEmpty(status)) { + statusView.setText(status); + } else { + statusView.setVisibility(View.GONE); + } + if (mIcon != null) { + icon.setImageDrawable(mIcon); + icon.setScaleType(ImageView.ScaleType.CENTER_CROP); + } else if (mIconResId != 0) { + icon.setImageDrawable(context.getDrawable(mIconResId)); + } + if (mMessage != null) { + messageView.setText(mMessage); + } else { + messageView.setText(mMessageResId); + } + + return v; + } +} diff --git a/core/java/com/android/internal/globalactions/ToggleAction.java b/core/java/com/android/internal/globalactions/ToggleAction.java new file mode 100644 index 000000000000..9167958612ea --- /dev/null +++ b/core/java/com/android/internal/globalactions/ToggleAction.java @@ -0,0 +1,155 @@ +/* + * 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.internal.globalactions; + +import android.content.Context; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import com.android.internal.R; + +/** + * A toggle action knows whether it is on or off, and displays an icon and status message + * accordingly. + */ +public abstract class ToggleAction implements Action { + private static final String TAG = "ToggleAction"; + + public enum State { + Off(false), + TurningOn(true), + TurningOff(true), + On(false); + + private final boolean inTransition; + + State(boolean intermediate) { + inTransition = intermediate; + } + + public boolean inTransition() { + return inTransition; + } + } + + protected State mState = State.Off; + + // prefs + protected int mEnabledIconResId; + protected int mDisabledIconResid; + protected int mMessageResId; + protected int mEnabledStatusMessageResId; + protected int mDisabledStatusMessageResId; + + /** + * @param enabledIconResId The icon for when this action is on. + * @param disabledIconResid The icon for when this action is off. + * @param message The general information message, e.g 'Silent Mode' + * @param enabledStatusMessageResId The on status message, e.g 'sound disabled' + * @param disabledStatusMessageResId The off status message, e.g. 'sound enabled' + */ + public ToggleAction(int enabledIconResId, + int disabledIconResid, + int message, + int enabledStatusMessageResId, + int disabledStatusMessageResId) { + mEnabledIconResId = enabledIconResId; + mDisabledIconResid = disabledIconResid; + mMessageResId = message; + mEnabledStatusMessageResId = enabledStatusMessageResId; + mDisabledStatusMessageResId = disabledStatusMessageResId; + } + + /** Override to make changes to resource IDs just before creating the View. */ + void willCreate() { + + } + + @Override + public CharSequence getLabelForAccessibility(Context context) { + return context.getString(mMessageResId); + } + + @Override + public View create(Context context, View convertView, ViewGroup parent, + LayoutInflater inflater) { + willCreate(); + + View v = inflater.inflate(R.layout.global_actions_item, parent, false); + + ImageView icon = v.findViewById(R.id.icon); + TextView messageView = v.findViewById(R.id.message); + TextView statusView = v.findViewById(R.id.status); + final boolean enabled = isEnabled(); + + if (messageView != null) { + messageView.setText(mMessageResId); + messageView.setEnabled(enabled); + } + + boolean on = ((mState == State.On) || (mState == State.TurningOn)); + if (icon != null) { + icon.setImageDrawable(context.getDrawable( + (on ? mEnabledIconResId : mDisabledIconResid))); + icon.setEnabled(enabled); + } + + if (statusView != null) { + statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId); + statusView.setVisibility(View.VISIBLE); + statusView.setEnabled(enabled); + } + v.setEnabled(enabled); + + return v; + } + + @Override + public final void onPress() { + if (mState.inTransition()) { + Log.w(TAG, "shouldn't be able to toggle when in transition"); + return; + } + + final boolean nowOn = !(mState == State.On); + onToggle(nowOn); + changeStateFromPress(nowOn); + } + + @Override + public boolean isEnabled() { + return !mState.inTransition(); + } + + /** + * Implementations may override this if their state can be in on of the intermediate + * states until some notification is received (e.g airplane mode is 'turning off' until + * we know the wireless connections are back online + * @param buttonOn Whether the button was turned on or off + */ + protected void changeStateFromPress(boolean buttonOn) { + mState = buttonOn ? State.On : State.Off; + } + + public abstract void onToggle(boolean on); + + public void updateState(State state) { + mState = state; + } +} diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java index 5457c1d4cc03..5b6291e4523f 100644 --- a/core/java/com/android/internal/os/BatterySipper.java +++ b/core/java/com/android/internal/os/BatterySipper.java @@ -113,19 +113,20 @@ public class BatterySipper implements Comparable<BatterySipper> { public double bluetoothPowerMah; public enum DrainType { - IDLE, - CELL, - PHONE, - WIFI, + AMBIENT_DISPLAY, + APP, BLUETOOTH, + CAMERA, + CELL, FLASHLIGHT, + IDLE, + MEMORY, + OVERCOUNTED, + PHONE, SCREEN, - APP, - USER, UNACCOUNTED, - OVERCOUNTED, - CAMERA, - MEMORY + USER, + WIFI, } public BatterySipper(DrainType drainType, Uid uid, double value) { diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index 5a59e70865e5..a76cf0a2ddc5 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -643,6 +643,21 @@ public class BatteryStatsHelper { } } + /** + * Ambient display power is the additional power the screen takes while in ambient display/ + * screen doze/ always-on display (interchangeable terms) mode. Ambient display power should + * be hidden {@link #shouldHideSipper(BatterySipper)}, but should not be included in smearing + * {@link #removeHiddenBatterySippers(List)}. + */ + private void addAmbientDisplayUsage() { + long ambientDisplayMs = mStats.getScreenDozeTime(mRawRealtimeUs, mStatsType); + double power = mPowerProfile.getAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY) + * ambientDisplayMs / (60 * 60 * 1000); + if (power > 0) { + addEntry(DrainType.AMBIENT_DISPLAY, ambientDisplayMs, power); + } + } + private void addRadioUsage() { BatterySipper radio = new BatterySipper(BatterySipper.DrainType.CELL, null, 0); mMobileRadioPowerCalculator.calculateRemaining(radio, mStats, mRawRealtimeUs, mRawUptimeUs, @@ -741,6 +756,7 @@ public class BatteryStatsHelper { addUserUsage(); addPhoneUsage(); addScreenUsage(); + addAmbientDisplayUsage(); addWiFiUsage(); addBluetoothUsage(); addMemoryUsage(); @@ -841,12 +857,13 @@ public class BatteryStatsHelper { final BatterySipper sipper = sippers.get(i); sipper.shouldHide = shouldHideSipper(sipper); if (sipper.shouldHide) { - if (sipper.drainType != BatterySipper.DrainType.OVERCOUNTED - && sipper.drainType != BatterySipper.DrainType.SCREEN - && sipper.drainType != BatterySipper.DrainType.UNACCOUNTED - && sipper.drainType != BatterySipper.DrainType.BLUETOOTH - && sipper.drainType != BatterySipper.DrainType.WIFI - && sipper.drainType != BatterySipper.DrainType.IDLE) { + if (sipper.drainType != DrainType.OVERCOUNTED + && sipper.drainType != DrainType.SCREEN + && sipper.drainType != DrainType.AMBIENT_DISPLAY + && sipper.drainType != DrainType.UNACCOUNTED + && sipper.drainType != DrainType.BLUETOOTH + && sipper.drainType != DrainType.WIFI + && sipper.drainType != DrainType.IDLE) { // Don't add it if it is overcounted, unaccounted or screen proportionalSmearPowerMah += sipper.totalPowerMah; } @@ -893,13 +910,14 @@ public class BatteryStatsHelper { * Check whether we should hide the battery sipper. */ public boolean shouldHideSipper(BatterySipper sipper) { - final BatterySipper.DrainType drainType = sipper.drainType; - - return drainType == BatterySipper.DrainType.IDLE - || drainType == BatterySipper.DrainType.CELL - || drainType == BatterySipper.DrainType.SCREEN - || drainType == BatterySipper.DrainType.UNACCOUNTED - || drainType == BatterySipper.DrainType.OVERCOUNTED + final DrainType drainType = sipper.drainType; + + return drainType == DrainType.IDLE + || drainType == DrainType.CELL + || drainType == DrainType.SCREEN + || drainType == DrainType.AMBIENT_DISPLAY + || drainType == DrainType.UNACCOUNTED + || drainType == DrainType.OVERCOUNTED || isTypeService(sipper) || isTypeSystem(sipper); } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 242f4229f0ae..4ab2fecbe964 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -7872,12 +7872,10 @@ public class BatteryStatsImpl extends BatteryStats { return; } final int deferredCount = mJobsDeferredCount.getCountLocked(which); - final long averageLatency = deferredEventCount != 0 - ? mJobsFreshnessTimeMs.getCountLocked(which) / deferredEventCount - : 0L; + final long totalLatency = mJobsFreshnessTimeMs.getCountLocked(which); sb.append(deferredEventCount); sb.append(','); sb.append(deferredCount); sb.append(','); - sb.append(averageLatency); + sb.append(totalLatency); for (int i = 0; i < JOB_FRESHNESS_BUCKETS.length; i++) { if (mJobsFreshnessBuckets[i] == null) { sb.append(",0"); @@ -7896,12 +7894,10 @@ public class BatteryStatsImpl extends BatteryStats { return; } final int deferredCount = mJobsDeferredCount.getCountLocked(which); - final long averageLatency = deferredEventCount != 0 - ? mJobsFreshnessTimeMs.getCountLocked(which) / deferredEventCount - : 0L; + final long totalLatency = mJobsFreshnessTimeMs.getCountLocked(which); sb.append("times="); sb.append(deferredEventCount); sb.append(", "); sb.append("count="); sb.append(deferredCount); sb.append(", "); - sb.append("avgLatency="); sb.append(averageLatency); sb.append(", "); + sb.append("totalLatencyMs="); sb.append(totalLatency); sb.append(", "); for (int i = 0; i < JOB_FRESHNESS_BUCKETS.length; i++) { sb.append("<"); sb.append(JOB_FRESHNESS_BUCKETS[i]); sb.append("ms="); if (mJobsFreshnessBuckets[i] == null) { diff --git a/core/java/com/android/internal/os/KernelCpuProcReader.java b/core/java/com/android/internal/os/KernelCpuProcReader.java index 4d569053979b..396deb4d7a0a 100644 --- a/core/java/com/android/internal/os/KernelCpuProcReader.java +++ b/core/java/com/android/internal/os/KernelCpuProcReader.java @@ -22,10 +22,12 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.FileChannel; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; @@ -124,6 +126,11 @@ public class KernelCpuProcReader { } fc.position(0); } + } catch (NoSuchFileException | FileNotFoundException e) { + // Happens when the kernel does not provide this file. Not a big issue. Just log it. + mErrors++; + Slog.w(TAG, "File not exist: " + mProc); + return null; } catch (IOException e) { mErrors++; Slog.e(TAG, "Error reading: " + mProc, e); diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index f4436d383a21..747d633ea1c3 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -104,7 +104,7 @@ public class PowerProfile { public static final String POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE = "modem.controller.voltage"; - /** + /** * Power consumption when GPS is on. */ public static final String POWER_GPS_ON = "gps.on"; @@ -136,6 +136,10 @@ public class PowerProfile { @Deprecated public static final String POWER_BLUETOOTH_AT_CMD = "bluetooth.at"; + /** + * Power consumption when screen is in doze/ambient/always-on mode, including backlight power. + */ + public static final String POWER_AMBIENT_DISPLAY = "ambient.on"; /** * Power consumption when screen is on, not including the backlight power. diff --git a/core/java/com/android/internal/textservice/LazyIntToIntMap.java b/core/java/com/android/internal/textservice/LazyIntToIntMap.java new file mode 100644 index 000000000000..ca9936c5fcda --- /dev/null +++ b/core/java/com/android/internal/textservice/LazyIntToIntMap.java @@ -0,0 +1,67 @@ +/* + * 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.internal.textservice; + +import android.annotation.NonNull; +import android.util.SparseIntArray; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.function.IntUnaryOperator; + +/** + * Simple int-to-int key-value-store that is to be lazily initialized with the given + * {@link IntUnaryOperator}. + */ +@VisibleForTesting +public final class LazyIntToIntMap { + + private final SparseIntArray mMap = new SparseIntArray(); + + @NonNull + private final IntUnaryOperator mMappingFunction; + + /** + * @param mappingFunction int to int mapping rules to be (lazily) evaluated + */ + public LazyIntToIntMap(@NonNull IntUnaryOperator mappingFunction) { + mMappingFunction = mappingFunction; + } + + /** + * Deletes {@code key} and associated value. + * @param key key to be deleted + */ + public void delete(int key) { + mMap.delete(key); + } + + /** + * @param key key associated with the value + * @return value associated with the {@code key}. If this is the first time to access + * {@code key}, then {@code mappingFunction} passed to the constructor will be evaluated + */ + public int get(int key) { + final int index = mMap.indexOfKey(key); + if (index >= 0) { + return mMap.valueAt(index); + } + final int value = mMappingFunction.applyAsInt(key); + mMap.append(key, value); + return value; + } +} diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java index 67dc81af5895..48485e0d95f6 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -37,6 +37,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.SubMenu; import android.view.View; +import android.view.ViewConfiguration; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -753,8 +754,7 @@ public class MenuBuilder implements Menu { private void setShortcutsVisibleInner(boolean shortcutsVisible) { mShortcutsVisible = shortcutsVisible && mResources.getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS - && mResources.getBoolean( - com.android.internal.R.bool.config_showMenuShortcutsWhenKeyboardPresent); + && ViewConfiguration.get(mContext).shouldShowMenuShortcutsWhenKeyboardPresent(); } /** diff --git a/core/jni/Android.bp b/core/jni/Android.bp index b048977ec87a..1d2209315849 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -110,8 +110,8 @@ cc_library_shared { "android_util_AssetManager.cpp", "android_util_Binder.cpp", "android_util_EventLog.cpp", - "android_util_MemoryIntArray.cpp", "android_util_Log.cpp", + "android_util_MemoryIntArray.cpp", "android_util_PathParser.cpp", "android_util_Process.cpp", "android_util_StringBlock.cpp", @@ -191,6 +191,7 @@ cc_library_shared { "android_backup_FileBackupHelperBase.cpp", "android_backup_BackupHelperDispatcher.cpp", "android_app_backup_FullBackup.cpp", + "android_content_res_ApkAssets.cpp", "android_content_res_ObbScanner.cpp", "android_content_res_Configuration.cpp", "android_animation_PropertyValuesHolder.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index f280c7a6b9da..5ae4a521ad6d 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -123,6 +123,7 @@ extern int register_android_util_MemoryIntArray(JNIEnv* env); extern int register_android_util_PathParser(JNIEnv* env); extern int register_android_content_StringBlock(JNIEnv* env); extern int register_android_content_XmlBlock(JNIEnv* env); +extern int register_android_content_res_ApkAssets(JNIEnv* env); extern int register_android_graphics_Canvas(JNIEnv* env); extern int register_android_graphics_CanvasProperty(JNIEnv* env); extern int register_android_graphics_ColorFilter(JNIEnv* env); @@ -1346,6 +1347,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_AssetManager), REG_JNI(register_android_content_StringBlock), REG_JNI(register_android_content_XmlBlock), + REG_JNI(register_android_content_res_ApkAssets), REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_Hyphenator), REG_JNI(register_android_text_MeasuredParagraph), diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp index 48aef4a8b320..ed032c78f6c7 100644 --- a/core/jni/android/graphics/FontFamily.cpp +++ b/core/jni/android/graphics/FontFamily.cpp @@ -28,7 +28,7 @@ #include <nativehelper/ScopedUtfChars.h> #include <android_runtime/AndroidRuntime.h> #include <android_runtime/android_util_AssetManager.h> -#include <androidfw/AssetManager.h> +#include <androidfw/AssetManager2.h> #include "Utils.h" #include "FontUtils.h" @@ -205,7 +205,8 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b NPE_CHECK_RETURN_ZERO(env, jpath); NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr); - AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr); + + Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, jassetMgr); if (NULL == mgr) { builder->axes.clear(); return false; @@ -217,27 +218,33 @@ static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong b return false; } - Asset* asset; - if (isAsset) { - asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); - } else { - asset = cookie ? mgr->openNonAsset(static_cast<int32_t>(cookie), str.c_str(), - Asset::ACCESS_BUFFER) : mgr->openNonAsset(str.c_str(), Asset::ACCESS_BUFFER); + std::unique_ptr<Asset> asset; + { + ScopedLock<AssetManager2> locked_mgr(*mgr); + if (isAsset) { + asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); + } else if (cookie > 0) { + // Valid java cookies are 1-based, but AssetManager cookies are 0-based. + asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast<ApkAssetsCookie>(cookie - 1), + Asset::ACCESS_BUFFER); + } else { + asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER); + } } - if (NULL == asset) { + if (nullptr == asset) { builder->axes.clear(); return false; } const void* buf = asset->getBuffer(false); if (NULL == buf) { - delete asset; builder->axes.clear(); return false; } - sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset)); + sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, + asset.release())); return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic); } diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 09e37e1a3de6..49a24a30f77e 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -361,7 +361,7 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName code->sdkVersion = sdkVersion; code->javaAssetManager = env->NewGlobalRef(jAssetMgr); - code->assetManager = assetManagerForJavaObject(env, jAssetMgr); + code->assetManager = NdkAssetManagerForJavaObject(env, jAssetMgr); if (obbDir != NULL) { dirStr = env->GetStringUTFChars(obbDir, NULL); diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp new file mode 100644 index 000000000000..7738d849be39 --- /dev/null +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_RESOURCES + +#include "android-base/macros.h" +#include "android-base/stringprintf.h" +#include "android-base/unique_fd.h" +#include "androidfw/ApkAssets.h" +#include "utils/misc.h" +#include "utils/Trace.h" + +#include "core_jni_helpers.h" +#include "jni.h" +#include "nativehelper/ScopedUtfChars.h" + +using ::android::base::unique_fd; + +namespace android { + +static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboolean system, + jboolean force_shared_lib, jboolean overlay) { + ScopedUtfChars path(env, java_path); + if (path.c_str() == nullptr) { + return 0; + } + + ATRACE_NAME(base::StringPrintf("LoadApkAssets(%s)", path.c_str()).c_str()); + + std::unique_ptr<const ApkAssets> apk_assets; + if (overlay) { + apk_assets = ApkAssets::LoadOverlay(path.c_str(), system); + } else if (force_shared_lib) { + apk_assets = ApkAssets::LoadAsSharedLibrary(path.c_str(), system); + } else { + apk_assets = ApkAssets::Load(path.c_str(), system); + } + + if (apk_assets == nullptr) { + std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str()); + jniThrowException(env, "java/io/IOException", error_msg.c_str()); + return 0; + } + return reinterpret_cast<jlong>(apk_assets.release()); +} + +static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor, + jstring friendly_name, jboolean system, jboolean force_shared_lib) { + ScopedUtfChars friendly_name_utf8(env, friendly_name); + if (friendly_name_utf8.c_str() == nullptr) { + return 0; + } + + ATRACE_NAME(base::StringPrintf("LoadApkAssetsFd(%s)", friendly_name_utf8.c_str()).c_str()); + + int fd = jniGetFDFromFileDescriptor(env, file_descriptor); + if (fd < 0) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); + return 0; + } + + unique_fd dup_fd(::dup(fd)); + if (dup_fd < 0) { + jniThrowIOException(env, errno); + return 0; + } + + std::unique_ptr<const ApkAssets> apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), + friendly_name_utf8.c_str(), + system, force_shared_lib); + if (apk_assets == nullptr) { + std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d", + friendly_name_utf8.c_str(), dup_fd.get()); + jniThrowException(env, "java/io/IOException", error_msg.c_str()); + return 0; + } + return reinterpret_cast<jlong>(apk_assets.release()); +} + +static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + delete reinterpret_cast<ApkAssets*>(ptr); +} + +static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr); + return env->NewStringUTF(apk_assets->GetPath().c_str()); +} + +static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr); + return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool()); +} + +static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr); + (void)apk_assets; + return JNI_TRUE; +} + +static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) { + ScopedUtfChars path_utf8(env, file_name); + if (path_utf8.c_str() == nullptr) { + return 0; + } + + const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr); + std::unique_ptr<Asset> asset = apk_assets->Open(path_utf8.c_str(), + Asset::AccessMode::ACCESS_RANDOM); + if (asset == nullptr) { + jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str()); + return 0; + } + + // DynamicRefTable is only needed when looking up resource references. Opening an XML file + // directly from an ApkAssets has no notion of proper resource references. + std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(nullptr /*dynamicRefTable*/); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); + asset.reset(); + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + return reinterpret_cast<jlong>(xml_tree.release()); +} + +// JNI registration. +static const JNINativeMethod gApkAssetsMethods[] = { + {"nativeLoad", "(Ljava/lang/String;ZZZ)J", (void*)NativeLoad}, + {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZ)J", + (void*)NativeLoadFromFd}, + {"nativeDestroy", "(J)V", (void*)NativeDestroy}, + {"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath}, + {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock}, + {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate}, + {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml}, +}; + +int register_android_content_res_ApkAssets(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods, + arraysize(gApkAssetsMethods)); +} + +} // namespace android diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 683b4c490ec3..8be6ed8c415d 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -1,1851 +1,1449 @@ -/* //device/libs/android_runtime/android_util_AssetManager.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ +/* + * Copyright 2006, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define ATRACE_TAG ATRACE_TAG_RESOURCES #define LOG_TAG "asset" -#include <android_runtime/android_util_AssetManager.h> - #include <inttypes.h> #include <linux/capability.h> #include <stdio.h> -#include <sys/types.h> -#include <sys/wait.h> #include <sys/stat.h> #include <sys/system_properties.h> +#include <sys/types.h> +#include <sys/wait.h> #include <private/android_filesystem_config.h> // for AID_SYSTEM +#include "android-base/logging.h" +#include "android-base/properties.h" +#include "android-base/stringprintf.h" +#include "android_runtime/android_util_AssetManager.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_util_Binder.h" #include "androidfw/Asset.h" #include "androidfw/AssetManager.h" +#include "androidfw/AssetManager2.h" #include "androidfw/AttributeResolution.h" +#include "androidfw/MutexGuard.h" #include "androidfw/ResourceTypes.h" -#include "android_runtime/AndroidRuntime.h" -#include "android_util_Binder.h" #include "core_jni_helpers.h" #include "jni.h" -#include <nativehelper/JNIHelp.h> -#include <nativehelper/ScopedStringChars.h> -#include <nativehelper/ScopedUtfChars.h> +#include "nativehelper/JNIHelp.h" +#include "nativehelper/ScopedPrimitiveArray.h" +#include "nativehelper/ScopedStringChars.h" +#include "nativehelper/ScopedUtfChars.h" #include "utils/Log.h" #include "utils/misc.h" #include "utils/String8.h" +#include "utils/Trace.h" extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap); extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap); +using ::android::base::StringPrintf; namespace android { -static const bool kThrowOnBadId = false; - // ---------------------------------------------------------------------------- -static struct typedvalue_offsets_t -{ - jfieldID mType; - jfieldID mData; - jfieldID mString; - jfieldID mAssetCookie; - jfieldID mResourceId; - jfieldID mChangingConfigurations; - jfieldID mDensity; +static struct typedvalue_offsets_t { + jfieldID mType; + jfieldID mData; + jfieldID mString; + jfieldID mAssetCookie; + jfieldID mResourceId; + jfieldID mChangingConfigurations; + jfieldID mDensity; } gTypedValueOffsets; -static struct assetfiledescriptor_offsets_t -{ - jfieldID mFd; - jfieldID mStartOffset; - jfieldID mLength; +static struct assetfiledescriptor_offsets_t { + jfieldID mFd; + jfieldID mStartOffset; + jfieldID mLength; } gAssetFileDescriptorOffsets; -static struct assetmanager_offsets_t -{ - jfieldID mObject; +static struct assetmanager_offsets_t { + jfieldID mObject; } gAssetManagerOffsets; -static struct sparsearray_offsets_t -{ - jclass classObject; - jmethodID constructor; - jmethodID put; +static struct { + jfieldID native_ptr; +} gApkAssetsFields; + +static struct sparsearray_offsets_t { + jclass classObject; + jmethodID constructor; + jmethodID put; } gSparseArrayOffsets; -static struct configuration_offsets_t -{ - jclass classObject; - jmethodID constructor; - jfieldID mSmallestScreenWidthDpOffset; - jfieldID mScreenWidthDpOffset; - jfieldID mScreenHeightDpOffset; +static struct configuration_offsets_t { + jclass classObject; + jmethodID constructor; + jfieldID mSmallestScreenWidthDpOffset; + jfieldID mScreenWidthDpOffset; + jfieldID mScreenHeightDpOffset; } gConfigurationOffsets; -jclass g_stringClass = NULL; +jclass g_stringClass = nullptr; // ---------------------------------------------------------------------------- -static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, - const Res_value& value, uint32_t ref, ssize_t block, - uint32_t typeSpecFlags, ResTable_config* config = NULL); - -jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, - const Res_value& value, uint32_t ref, ssize_t block, - uint32_t typeSpecFlags, ResTable_config* config) -{ - env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType); - env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie, - static_cast<jint>(table->getTableCookie(block))); - env->SetIntField(outValue, gTypedValueOffsets.mData, value.data); - env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL); - env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref); - env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations, - typeSpecFlags); - if (config != NULL) { - env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density); - } - return block; +// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. +constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { + return cookie != kInvalidCookie ? static_cast<jint>(cookie + 1) : -1; } -// This is called by zygote (running as user root) as part of preloadResources. -static void verifySystemIdmaps() -{ - pid_t pid; - char system_id[10]; - - snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM); - - switch (pid = fork()) { - case -1: - ALOGE("failed to fork for idmap: %s", strerror(errno)); - break; - case 0: // child - { - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata; - - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - - capheader.version = _LINUX_CAPABILITY_VERSION; - capheader.pid = 0; - - if (capget(&capheader, &capdata) != 0) { - ALOGE("capget: %s\n", strerror(errno)); - exit(1); - } - - capdata.effective = capdata.permitted; - if (capset(&capheader, &capdata) != 0) { - ALOGE("capset: %s\n", strerror(errno)); - exit(1); - } - - if (setgid(AID_SYSTEM) != 0) { - ALOGE("setgid: %s\n", strerror(errno)); - exit(1); - } - - if (setuid(AID_SYSTEM) != 0) { - ALOGE("setuid: %s\n", strerror(errno)); - exit(1); - } - - // Generic idmap parameters - const char* argv[8]; - int argc = 0; - struct stat st; - - memset(argv, NULL, sizeof(argv)); - argv[argc++] = AssetManager::IDMAP_BIN; - argv[argc++] = "--scan"; - argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; - argv[argc++] = AssetManager::TARGET_APK_PATH; - argv[argc++] = AssetManager::IDMAP_DIR; - - // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, - // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR. - char subdir[PROP_VALUE_MAX]; - int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir); - if (len > 0) { - String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir; - if (stat(overlayPath.string(), &st) == 0) { - argv[argc++] = overlayPath.string(); - } - } - if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::OVERLAY_DIR; - } - - if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; - } - - // Finally, invoke idmap (if any overlay directory exists) - if (argc > 5) { - execv(AssetManager::IDMAP_BIN, (char* const*)argv); - ALOGE("failed to execv for idmap: %s", strerror(errno)); - exit(1); // should never get here - } else { - exit(0); - } - } - break; - default: // parent - waitpid(pid, NULL, 0); - break; - } +constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) { + return cookie > 0 ? static_cast<ApkAssetsCookie>(cookie - 1) : kInvalidCookie; } - -// ---------------------------------------------------------------------------- - -// this guy is exported to other jni routines -AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj) -{ - jlong amHandle = env->GetLongField(obj, gAssetManagerOffsets.mObject); - AssetManager* am = reinterpret_cast<AssetManager*>(amHandle); - if (am != NULL) { - return am; - } - jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); - return NULL; +// This is called by zygote (running as user root) as part of preloadResources. +static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { + switch (pid_t pid = fork()) { + case -1: + PLOG(ERROR) << "failed to fork for idmap"; + break; + + // child + case 0: { + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata; + + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + + capheader.version = _LINUX_CAPABILITY_VERSION; + capheader.pid = 0; + + if (capget(&capheader, &capdata) != 0) { + PLOG(ERROR) << "capget"; + exit(1); + } + + capdata.effective = capdata.permitted; + if (capset(&capheader, &capdata) != 0) { + PLOG(ERROR) << "capset"; + exit(1); + } + + if (setgid(AID_SYSTEM) != 0) { + PLOG(ERROR) << "setgid"; + exit(1); + } + + if (setuid(AID_SYSTEM) != 0) { + PLOG(ERROR) << "setuid"; + exit(1); + } + + // Generic idmap parameters + const char* argv[8]; + int argc = 0; + struct stat st; + + memset(argv, 0, sizeof(argv)); + argv[argc++] = AssetManager::IDMAP_BIN; + argv[argc++] = "--scan"; + argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; + argv[argc++] = AssetManager::TARGET_APK_PATH; + argv[argc++] = AssetManager::IDMAP_DIR; + + // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined, + // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR. + std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY, + ""); + if (!overlay_theme_path.empty()) { + overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path; + if (stat(overlay_theme_path.c_str(), &st) == 0) { + argv[argc++] = overlay_theme_path.c_str(); + } + } + + if (stat(AssetManager::OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::OVERLAY_DIR; + } + + if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; + } + + // Finally, invoke idmap (if any overlay directory exists) + if (argc > 5) { + execv(AssetManager::IDMAP_BIN, (char* const*)argv); + PLOG(ERROR) << "failed to execv for idmap"; + exit(1); // should never get here + } else { + exit(0); + } + } break; + + // parent + default: + waitpid(pid, nullptr, 0); + break; + } } -static jlong android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz, - jstring fileName, jint mode) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openAsset in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Empty file name"); - return -1; - } - - if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM - && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return -1; - } - - Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return -1; - } - - //printf("Created Asset Stream: %p\n", a); - - return reinterpret_cast<jlong>(a); +static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref, + uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType); + env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie, + ApkAssetsCookieToJavaCookie(cookie)); + env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data); + env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr); + env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref); + env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags); + if (config != nullptr) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density); + } + return static_cast<jint>(ApkAssetsCookieToJavaCookie(cookie)); } -static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets) -{ - off64_t startOffset, length; - int fd = a->openFileDescriptor(&startOffset, &length); - delete a; - - if (fd < 0) { - jniThrowException(env, "java/io/FileNotFoundException", - "This file can not be opened as a file descriptor; it is probably compressed"); - return NULL; - } - - jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0); - if (offsets == NULL) { - close(fd); - return NULL; - } - - offsets[0] = startOffset; - offsets[1] = length; - - env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0); +// ---------------------------------------------------------------------------- - jobject fileDesc = jniCreateFileDescriptor(env, fd); - if (fileDesc == NULL) { - close(fd); - return NULL; - } +// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance. +struct GuardedAssetManager : public ::AAssetManager { + Guarded<AssetManager2> guarded_assetmanager; +}; - return newParcelFileDescriptor(env, fileDesc); +::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { + jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject); + ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle); + if (am == nullptr) { + jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!"); + return nullptr; + } + return am; } -static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz, - jstring fileName, jlongArray outOffsets) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ALOGV("openAssetFd in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - //printf("Created Asset Stream: %p\n", a); - - return returnParcelFileDescriptor(env, a, outOffsets); +Guarded<AssetManager2>* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) { + if (assetmanager == nullptr) { + return nullptr; + } + return &reinterpret_cast<GuardedAssetManager*>(assetmanager)->guarded_assetmanager; } -static jlong android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName, - jint mode) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return -1; - } - - if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM - && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); - return -1; - } - - Asset* a = cookie - ? am->openNonAsset(static_cast<int32_t>(cookie), fileName8.c_str(), - (Asset::AccessMode)mode) - : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return -1; - } - - //printf("Created Asset Stream: %p\n", a); - - return reinterpret_cast<jlong>(a); +Guarded<AssetManager2>* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) { + return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager)); } -static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName, - jlongArray outOffsets) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ALOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - Asset* a = cookie - ? am->openNonAsset(static_cast<int32_t>(cookie), fileName8.c_str(), Asset::ACCESS_RANDOM) - : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - //printf("Created Asset Stream: %p\n", a); - - return returnParcelFileDescriptor(env, a, outOffsets); +static Guarded<AssetManager2>& AssetManagerFromLong(jlong ptr) { + return *AssetManagerForNdkAssetManager(reinterpret_cast<AAssetManager*>(ptr)); } -static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz, - jstring fileName) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return NULL; - } - - AssetDir* dir = am->openDir(fileName8.c_str()); - - if (dir == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return NULL; - } - - size_t N = dir->getFileCount(); - - jobjectArray array = env->NewObjectArray(dir->getFileCount(), - g_stringClass, NULL); - if (array == NULL) { - delete dir; - return NULL; - } - - for (size_t i=0; i<N; i++) { - const String8& name = dir->getFileName(i); - jstring str = env->NewStringUTF(name.string()); - if (str == NULL) { - delete dir; - return NULL; - } - env->SetObjectArrayElement(array, i, str); - env->DeleteLocalRef(str); - } - - delete dir; - - return array; +static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr<Asset> asset, + jlongArray out_offsets) { + off64_t start_offset, length; + int fd = asset->openFileDescriptor(&start_offset, &length); + asset.reset(); + + if (fd < 0) { + jniThrowException(env, "java/io/FileNotFoundException", + "This file can not be opened as a file descriptor; it is probably " + "compressed"); + return nullptr; + } + + jlong* offsets = reinterpret_cast<jlong*>(env->GetPrimitiveArrayCritical(out_offsets, 0)); + if (offsets == nullptr) { + close(fd); + return nullptr; + } + + offsets[0] = start_offset; + offsets[1] = length; + + env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0); + + jobject file_desc = jniCreateFileDescriptor(env, fd); + if (file_desc == nullptr) { + close(fd); + return nullptr; + } + return newParcelFileDescriptor(env, file_desc); } -static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast<Asset*>(assetHandle); - - //printf("Destroying Asset Stream: %p\n", a); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return; - } - - delete a; +static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) { + return Asset::getGlobalCount(); } -static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast<Asset*>(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - uint8_t b; - ssize_t res = a->read(&b, 1); - return res == 1 ? b : -1; +static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) { + String8 alloc = Asset::getAssetAllocations(); + if (alloc.length() <= 0) { + return nullptr; + } + return env->NewStringUTF(alloc.string()); } -static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz, - jlong assetHandle, jbyteArray bArray, - jint off, jint len) -{ - Asset* a = reinterpret_cast<Asset*>(assetHandle); - - if (a == NULL || bArray == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - if (len == 0) { - return 0; - } - - jsize bLen = env->GetArrayLength(bArray); - if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); - return -1; - } - - jbyte* b = env->GetByteArrayElements(bArray, NULL); - ssize_t res = a->read(b+off, len); - env->ReleaseByteArrayElements(bArray, b, 0); - - if (res > 0) return static_cast<jint>(res); - - if (res < 0) { - jniThrowException(env, "java/io/IOException", ""); - } - return -1; +static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) { + // TODO(adamlesinski): Switch to AssetManager2. + return AssetManager::getGlobalCount(); } -static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz, - jlong assetHandle, - jlong offset, jint whence) -{ - Asset* a = reinterpret_cast<Asset*>(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - return a->seek( - offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)); +static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) { + // AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and + // AssetManager2 in a contiguous block (GuardedAssetManager). + return reinterpret_cast<jlong>(new GuardedAssetManager()); } -static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast<Asset*>(assetHandle); - - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; - } - - return a->getLength(); +static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + delete reinterpret_cast<GuardedAssetManager*>(ptr); } -static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz, - jlong assetHandle) -{ - Asset* a = reinterpret_cast<Asset*>(assetHandle); +static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jobjectArray apk_assets_array, jboolean invalidate_caches) { + ATRACE_NAME("AssetManager::SetApkAssets"); - if (a == NULL) { - jniThrowNullPointerException(env, "asset"); - return -1; + const jsize apk_assets_len = env->GetArrayLength(apk_assets_array); + std::vector<const ApkAssets*> apk_assets; + apk_assets.reserve(apk_assets_len); + for (jsize i = 0; i < apk_assets_len; i++) { + jobject obj = env->GetObjectArrayElement(apk_assets_array, i); + if (obj == nullptr) { + std::string msg = StringPrintf("ApkAssets at index %d is null", i); + jniThrowNullPointerException(env, msg.c_str()); + return; } - return a->getRemainingLength(); -} - -static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz, - jstring path, jboolean appAsLib) -{ - ScopedUtfChars path8(env, path); - if (path8.c_str() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; + jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr); + if (env->ExceptionCheck()) { + return; } + apk_assets.push_back(reinterpret_cast<const ApkAssets*>(apk_assets_native_ptr)); + } - int32_t cookie; - bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib); - - return (res) ? static_cast<jint>(cookie) : 0; + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + assetmanager->SetApkAssets(apk_assets, invalidate_caches); } -static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz, - jstring idmapPath) -{ - ScopedUtfChars idmapPath8(env, idmapPath); - if (idmapPath8.c_str() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - int32_t cookie; - bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie); - - return (res) ? (jint)cookie : 0; +static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc, + jstring locale, jint orientation, jint touchscreen, jint density, + jint keyboard, jint keyboard_hidden, jint navigation, + jint screen_width, jint screen_height, + jint smallest_screen_width_dp, jint screen_width_dp, + jint screen_height_dp, jint screen_layout, jint ui_mode, + jint color_mode, jint major_version) { + ATRACE_NAME("AssetManager::SetConfiguration"); + + ResTable_config configuration; + memset(&configuration, 0, sizeof(configuration)); + configuration.mcc = static_cast<uint16_t>(mcc); + configuration.mnc = static_cast<uint16_t>(mnc); + configuration.orientation = static_cast<uint8_t>(orientation); + configuration.touchscreen = static_cast<uint8_t>(touchscreen); + configuration.density = static_cast<uint16_t>(density); + configuration.keyboard = static_cast<uint8_t>(keyboard); + configuration.inputFlags = static_cast<uint8_t>(keyboard_hidden); + configuration.navigation = static_cast<uint8_t>(navigation); + configuration.screenWidth = static_cast<uint16_t>(screen_width); + configuration.screenHeight = static_cast<uint16_t>(screen_height); + configuration.smallestScreenWidthDp = static_cast<uint16_t>(smallest_screen_width_dp); + configuration.screenWidthDp = static_cast<uint16_t>(screen_width_dp); + configuration.screenHeightDp = static_cast<uint16_t>(screen_height_dp); + configuration.screenLayout = static_cast<uint8_t>(screen_layout); + configuration.uiMode = static_cast<uint8_t>(ui_mode); + configuration.colorMode = static_cast<uint8_t>(color_mode); + configuration.sdkVersion = static_cast<uint16_t>(major_version); + + if (locale != nullptr) { + ScopedUtfChars locale_utf8(env, locale); + CHECK(locale_utf8.c_str() != nullptr); + configuration.setBcp47Locale(locale_utf8.c_str()); + } + + // Constants duplicated from Java class android.content.res.Configuration. + static const jint kScreenLayoutRoundMask = 0x300; + static const jint kScreenLayoutRoundShift = 8; + + // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer + // in C++. We must extract the round qualifier out of the Java screenLayout and put it + // into screenLayout2. + configuration.screenLayout2 = + static_cast<uint8_t>((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); + + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + assetmanager->SetConfiguration(configuration); } -static jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz, - jobject fileDescriptor, jstring debugPathName, - jboolean appAsLib) -{ - ScopedUtfChars debugPathName8(env, debugPathName); +static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); - if (fd < 0) { - jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor"); - return 0; - } + jobject sparse_array = + env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor); - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } + if (sparse_array == nullptr) { + // An exception is pending. + return nullptr; + } - int dupfd = ::dup(fd); - if (dupfd < 0) { - jniThrowIOException(env, errno); - return 0; + assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) { + jstring jpackage_name = env->NewStringUTF(package_name.c_str()); + if (jpackage_name == nullptr) { + // An exception is pending. + return; } - int32_t cookie; - bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib); - - return (res) ? static_cast<jint>(cookie) : 0; + env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast<jint>(package_id), + jpackage_name); + }); + return sparse_array; } -static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_TRUE; - } - return am->isUpToDate() ? JNI_TRUE : JNI_FALSE; -} +static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) { + ScopedUtfChars path_utf8(env, path); + if (path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } -static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales) -{ - Vector<String8> locales; + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr<AssetDir> asset_dir = + assetmanager->OpenDir(path_utf8.c_str()); + if (asset_dir == nullptr) { + jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str()); + return nullptr; + } - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } + const size_t file_count = asset_dir->getFileCount(); - am->getLocales(&locales, includeSystemLocales); + jobjectArray array = env->NewObjectArray(file_count, g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } - const int N = locales.size(); + for (size_t i = 0; i < file_count; i++) { + jstring java_string = env->NewStringUTF(asset_dir->getFileName(i).string()); - jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL); - if (result == NULL) { - return NULL; + // Check for errors creating the strings (if malformed or no memory). + if (env->ExceptionCheck()) { + return nullptr; } - for (int i=0; i<N; i++) { - jstring str = env->NewStringUTF(locales[i].string()); - if (str == NULL) { - return NULL; - } - env->SetObjectArrayElement(result, i, str); - env->DeleteLocalRef(str); - } + env->SetObjectArrayElement(array, i, java_string); - return result; + // If we have a large amount of string in our array, we might overflow the + // local reference table of the VM. + env->DeleteLocalRef(java_string); + } + return array; } -static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz) -{ - return getLocales(env, clazz, true /* include system locales */); +static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, + jint access_mode) { + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + ATRACE_NAME(base::StringPrintf("AssetManager::OpenAsset(%s)", asset_path_utf8.c_str()).c_str()); + + if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && + access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return 0; + } + + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr<Asset> asset = + assetmanager->Open(asset_path_utf8.c_str(), static_cast<Asset::AccessMode>(access_mode)); + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + return reinterpret_cast<jlong>(asset.release()); } -static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz) -{ - return getLocales(env, clazz, false /* don't include system locales */); +static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path, + jlongArray out_offsets) { + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + ATRACE_NAME(base::StringPrintf("AssetManager::OpenAssetFd(%s)", asset_path_utf8.c_str()).c_str()); + + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr<Asset> asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return nullptr; + } + return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); } -static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) { - jobject result = env->NewObject(gConfigurationOffsets.classObject, - gConfigurationOffsets.constructor); - if (result == NULL) { - return NULL; - } - - env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, - config.smallestScreenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); - env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); - - return result; +static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path, jint access_mode) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + ATRACE_NAME(base::StringPrintf("AssetManager::OpenNonAsset(%s)", asset_path_utf8.c_str()).c_str()); + + if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM && + access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode"); + return 0; + } + + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr<Asset> asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, + static_cast<Asset::AccessMode>(access_mode)); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), + static_cast<Asset::AccessMode>(access_mode)); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + return reinterpret_cast<jlong>(asset.release()); } -static jobjectArray getSizeConfigurationsInternal(JNIEnv* env, - const Vector<ResTable_config>& configs) { - const int N = configs.size(); - jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL); - if (result == NULL) { - return NULL; - } - - for (int i=0; i<N; i++) { - jobject config = constructConfigurationObject(env, configs[i]); - if (config == NULL) { - env->DeleteLocalRef(result); - return NULL; - } - - env->SetObjectArrayElement(result, i, config); - env->DeleteLocalRef(config); - } - - return result; +static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path, jlongArray out_offsets) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return nullptr; + } + + ATRACE_NAME(base::StringPrintf("AssetManager::OpenNonAssetFd(%s)", asset_path_utf8.c_str()).c_str()); + + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr<Asset> asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return nullptr; + } + return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets); } -static jobjectArray android_content_AssetManager_getSizeConfigurations(JNIEnv* env, jobject clazz) { - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - const ResTable& res(am->getResources()); - Vector<ResTable_config> configs; - res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */); - - return getSizeConfigurationsInternal(env, configs); +static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie, + jstring asset_path) { + ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); + ScopedUtfChars asset_path_utf8(env, asset_path); + if (asset_path_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + ATRACE_NAME(base::StringPrintf("AssetManager::OpenXmlAsset(%s)", asset_path_utf8.c_str()).c_str()); + + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + std::unique_ptr<Asset> asset; + if (cookie != kInvalidCookie) { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM); + } else { + asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie); + } + + if (!asset) { + jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str()); + return 0; + } + + // May be nullptr. + const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie); + + std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(dynamic_ref_table); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); + asset.reset(); + + if (err != NO_ERROR) { + jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); + return 0; + } + return reinterpret_cast<jlong>(xml_tree.release()); } -static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz, - jint mcc, jint mnc, - jstring locale, jint orientation, - jint touchscreen, jint density, - jint keyboard, jint keyboardHidden, - jint navigation, - jint screenWidth, jint screenHeight, - jint smallestScreenWidthDp, - jint screenWidthDp, jint screenHeightDp, - jint screenLayout, jint uiMode, - jint colorMode, jint sdkVersion) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return; - } - - ResTable_config config; - memset(&config, 0, sizeof(config)); - - const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL; - - // Constants duplicated from Java class android.content.res.Configuration. - static const jint kScreenLayoutRoundMask = 0x300; - static const jint kScreenLayoutRoundShift = 8; - - config.mcc = (uint16_t)mcc; - config.mnc = (uint16_t)mnc; - config.orientation = (uint8_t)orientation; - config.touchscreen = (uint8_t)touchscreen; - config.density = (uint16_t)density; - config.keyboard = (uint8_t)keyboard; - config.inputFlags = (uint8_t)keyboardHidden; - config.navigation = (uint8_t)navigation; - config.screenWidth = (uint16_t)screenWidth; - config.screenHeight = (uint16_t)screenHeight; - config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp; - config.screenWidthDp = (uint16_t)screenWidthDp; - config.screenHeightDp = (uint16_t)screenHeightDp; - config.screenLayout = (uint8_t)screenLayout; - config.uiMode = (uint8_t)uiMode; - config.colorMode = (uint8_t)colorMode; - config.sdkVersion = (uint16_t)sdkVersion; - config.minorVersion = 0; - - // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer - // in C++. We must extract the round qualifier out of the Java screenLayout and put it - // into screenLayout2. - config.screenLayout2 = - (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); - - am->setConfiguration(config, locale8); - - if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8); +static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jshort density, jobject typed_value, + jboolean resolve_references) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + Res_value value; + ResTable_config selected_config; + uint32_t flags; + ApkAssetsCookie cookie = + assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/, + static_cast<uint16_t>(density), &value, &selected_config, &flags); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t ref = static_cast<uint32_t>(resid); + if (resolve_references) { + cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + } + return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value); } -static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz, - jstring name, - jstring defType, - jstring defPackage) -{ - ScopedStringChars name16(env, name); - if (name16.get() == NULL) { - return 0; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - const char16_t* defType16 = reinterpret_cast<const char16_t*>(defType) - ? reinterpret_cast<const char16_t*>(env->GetStringChars(defType, NULL)) - : NULL; - jsize defTypeLen = defType - ? env->GetStringLength(defType) : 0; - const char16_t* defPackage16 = reinterpret_cast<const char16_t*>(defPackage) - ? reinterpret_cast<const char16_t*>(env->GetStringChars(defPackage, - NULL)) - : NULL; - jsize defPackageLen = defPackage - ? env->GetStringLength(defPackage) : 0; - - jint ident = am->getResources().identifierForName( - reinterpret_cast<const char16_t*>(name16.get()), name16.size(), - defType16, defTypeLen, defPackage16, defPackageLen); - - if (defPackage16) { - env->ReleaseStringChars(defPackage, - reinterpret_cast<const jchar*>(defPackage16)); - } - if (defType16) { - env->ReleaseStringChars(defType, - reinterpret_cast<const jchar*>(defType16)); - } - - return ident; +static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jint bag_entry_id, jobject typed_value) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); + if (bag == nullptr) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t type_spec_flags = bag->type_spec_flags; + ApkAssetsCookie cookie = kInvalidCookie; + const Res_value* bag_value = nullptr; + for (const ResolvedBag::Entry& entry : bag) { + if (entry.key == static_cast<uint32_t>(bag_entry_id)) { + cookie = entry.cookie; + bag_value = &entry.value; + + // Keep searching (the old implementation did that). + } + } + + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + Res_value value = *bag_value; + uint32_t ref = static_cast<uint32_t>(resid); + ResTable_config selected_config; + cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value); } -static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - String16 str; - if (name.package != NULL) { - str.setTo(name.package, name.packageLen); - } - if (name.type8 != NULL || name.type != NULL) { - if (str.size() > 0) { - char16_t div = ':'; - str.append(&div, 1); - } - if (name.type8 != NULL) { - str.append(String16(name.type8, name.typeLen)); - } else { - str.append(name.type, name.typeLen); - } - } - if (name.name8 != NULL || name.name != NULL) { - if (str.size() > 0) { - char16_t div = '/'; - str.append(&div, 1); - } - if (name.name8 != NULL) { - str.append(String16(name.name8, name.nameLen)); - } else { - str.append(name.name, name.nameLen); - } - } - - return env->NewString((const jchar*)str.string(), str.size()); +static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count); + if (env->ExceptionCheck()) { + return nullptr; + } + + for (uint32_t i = 0; i < bag->entry_count; i++) { + jint attr_resid = bag->entries[i].key; + env->SetIntArrayRegion(array, i, 1, &attr_resid); + } + return array; } -static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - if (name.package != NULL) { - return env->NewString((const jchar*)name.package, name.packageLen); - } - - return NULL; +static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jint resid) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); + if (bag == nullptr) { + return nullptr; + } + + jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } + + for (uint32_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + + // Resolve any references to their final value. + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return nullptr; + } + + if (value.dataType == Res_value::TYPE_STRING) { + const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie]; + const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool(); + + jstring java_string = nullptr; + size_t str_len; + const char* str_utf8 = pool->string8At(value.data, &str_len); + if (str_utf8 != nullptr) { + java_string = env->NewStringUTF(str_utf8); + } else { + const char16_t* str_utf16 = pool->stringAt(value.data, &str_len); + java_string = env->NewString(reinterpret_cast<const jchar*>(str_utf16), str_len); + } + + // Check for errors creating the strings (if malformed or no memory). + if (env->ExceptionCheck()) { + return nullptr; + } + + env->SetObjectArrayElement(array, i, java_string); + + // If we have a large amount of string in our array, we might overflow the + // local reference table of the VM. + env->DeleteLocalRef(java_string); + } + } + return array; } -static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } - - if (name.type8 != NULL) { - return env->NewStringUTF(name.type8); - } - - if (name.type != NULL) { - return env->NewString((const jchar*)name.type, name.typeLen); - } +static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jint resid) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count * 2); + if (array == nullptr) { + return nullptr; + } + + jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(array, nullptr)); + if (buffer == nullptr) { + return nullptr; + } + + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); + return nullptr; + } + + jint string_index = -1; + if (value.dataType == Res_value::TYPE_STRING) { + string_index = static_cast<jint>(value.data); + } + + buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie); + buffer[(i * 2) + 1] = string_index; + } + env->ReleasePrimitiveArrayCritical(array, buffer, 0); + return array; +} - return NULL; +static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); + if (bag == nullptr) { + return nullptr; + } + + jintArray array = env->NewIntArray(bag->entry_count); + if (array == nullptr) { + return nullptr; + } + + jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(array, nullptr)); + if (buffer == nullptr) { + return nullptr; + } + + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); + return nullptr; + } + + if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) { + buffer[i] = static_cast<jint>(value.data); + } + } + env->ReleasePrimitiveArrayCritical(array, buffer, 0); + return array; } -static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz, - jint resid) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } +static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); + if (bag == nullptr) { + return -1; + } + return static_cast<jint>(bag->entry_count); +} - ResTable::resource_name name; - if (!am->getResources().getResourceName(resid, true, &name)) { - return NULL; - } +static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, + jintArray out_data) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); + if (bag == nullptr) { + return -1; + } - if (name.name8 != NULL) { - return env->NewStringUTF(name.name8); - } + const jsize out_data_length = env->GetArrayLength(out_data); + if (env->ExceptionCheck()) { + return -1; + } - if (name.name != NULL) { - return env->NewString((const jchar*)name.name, name.nameLen); - } + if (static_cast<jsize>(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) { + jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough"); + return -1; + } - return NULL; + jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_data, nullptr)); + if (buffer == nullptr) { + return -1; + } + + jint* cursor = buffer; + for (size_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + selected_config.density = 0; + uint32_t flags = bag->type_spec_flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT); + return -1; + } + + // Deal with the special @null value -- it turns back to TYPE_NULL. + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + } + + cursor[STYLE_TYPE] = static_cast<jint>(value.dataType); + cursor[STYLE_DATA] = static_cast<jint>(value.data); + cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + cursor[STYLE_RESOURCE_ID] = static_cast<jint>(ref); + cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast<jint>(flags); + cursor[STYLE_DENSITY] = static_cast<jint>(selected_config.density); + cursor += STYLE_NUM_ENTRIES; + } + env->ReleasePrimitiveArrayCritical(out_data, buffer, 0); + return static_cast<jint>(bag->entry_count); } -static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz, - jint ident, - jshort density, - jobject outValue, - jboolean resolve) -{ - if (outValue == NULL) { - jniThrowNullPointerException(env, "outValue"); - return 0; - } - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - Res_value value; - ResTable_config config; - uint32_t typeSpecFlags; - ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - uint32_t ref = ident; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - } - if (block >= 0) { - return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config); - } - - return static_cast<jint>(block); +static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name, + jstring def_type, jstring def_package) { + ScopedUtfChars name_utf8(env, name); + if (name_utf8.c_str() == nullptr) { + // This will throw NPE. + return 0; + } + + std::string type; + if (def_type != nullptr) { + ScopedUtfChars type_utf8(env, def_type); + CHECK(type_utf8.c_str() != nullptr); + type = type_utf8.c_str(); + } + + std::string package; + if (def_package != nullptr) { + ScopedUtfChars package_utf8(env, def_package); + CHECK(package_utf8.c_str() != nullptr); + package = package_utf8.c_str(); + } + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + return static_cast<jint>(assetmanager->GetResourceId(name_utf8.c_str(), type, package)); } -static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz, - jint ident, jint bagEntryId, - jobject outValue, jboolean resolve) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - - ssize_t block = -1; - Res_value value; +static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) { + return nullptr; + } - const ResTable::bag_entry* entry = NULL; - uint32_t typeSpecFlags; - ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags); + std::string result; + if (name.package != nullptr) { + result.append(name.package, name.package_len); + } - for (ssize_t i=0; i<entryCount; i++) { - if (((uint32_t)bagEntryId) == entry->map.name.ident) { - block = entry->stringBlock; - value = entry->map.value; - } - entry++; + if (name.type != nullptr || name.type16 != nullptr) { + if (!result.empty()) { + result += ":"; } - res.unlock(); - - if (block < 0) { - return static_cast<jint>(block); + if (name.type != nullptr) { + result.append(name.type, name.type_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len)); } + } - uint32_t ref = ident; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } - } - if (block >= 0) { - return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags); + if (name.entry != nullptr || name.entry16 != nullptr) { + if (!result.empty()) { + result += "/"; } - return static_cast<jint>(block); -} - -static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; + if (name.entry != nullptr) { + result.append(name.entry, name.entry_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); } - return am->getResources().getTableCount(); + } + return env->NewStringUTF(result.c_str()); } -static jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz, - jint block) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - return reinterpret_cast<jlong>(am->getResources().getTableStringBlock(block)); +static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) { + return nullptr; + } + + if (name.package != nullptr) { + return env->NewStringUTF(name.package); + } + return nullptr; } -static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz, - jint cookie) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - String8 name(am->getAssetPath(static_cast<int32_t>(cookie))); - if (name.length() == 0) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name"); - return NULL; - } - jstring str = env->NewStringUTF(name.string()); - return str; +static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) { + return nullptr; + } + + if (name.type != nullptr) { + return env->NewStringUTF(name.type); + } else if (name.type16 != nullptr) { + return env->NewString(reinterpret_cast<const jchar*>(name.type16), name.type_len); + } + return nullptr; } -static jobject android_content_AssetManager_getAssignedPackageIdentifiers(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - const ResTable& res = am->getResources(); - - jobject sparseArray = env->NewObject(gSparseArrayOffsets.classObject, - gSparseArrayOffsets.constructor); - const size_t N = res.getBasePackageCount(); - for (size_t i = 0; i < N; i++) { - const String16 name = res.getBasePackageName(i); - env->CallVoidMethod( - sparseArray, gSparseArrayOffsets.put, - static_cast<jint>(res.getBasePackageId(i)), - env->NewString(reinterpret_cast<const jchar*>(name.string()), - name.size())); - } - return sparseArray; +static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) { + return nullptr; + } + + if (name.entry != nullptr) { + return env->NewStringUTF(name.entry); + } else if (name.entry16 != nullptr) { + return env->NewString(reinterpret_cast<const jchar*>(name.entry16), name.entry_len); + } + return nullptr; } -static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - return reinterpret_cast<jlong>(new ResTable::Theme(am->getResources())); +static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr, + jboolean exclude_system) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + std::set<std::string> locales = + assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/); + + jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr); + if (array == nullptr) { + return nullptr; + } + + size_t idx = 0; + for (const std::string& locale : locales) { + jstring java_string = env->NewStringUTF(locale.c_str()); + if (java_string == nullptr) { + return nullptr; + } + env->SetObjectArrayElement(array, idx++, java_string); + env->DeleteLocalRef(java_string); + } + return array; } -static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz, - jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle); - delete theme; +static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) { + jobject result = + env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor); + if (result == nullptr) { + return nullptr; + } + + env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset, + config.smallestScreenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp); + env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp); + return result; } -static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz, - jlong themeHandle, - jint styleRes, - jboolean force) -{ - ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle); - theme->applyStyle(styleRes, force ? true : false); -} +static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + std::set<ResTable_config> configurations = + assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/); -static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz, - jlong destHandle, jlong srcHandle) -{ - ResTable::Theme* dest = reinterpret_cast<ResTable::Theme*>(destHandle); - ResTable::Theme* src = reinterpret_cast<ResTable::Theme*>(srcHandle); - dest->setTo(*src); -} + jobjectArray array = + env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr); + if (array == nullptr) { + return nullptr; + } -static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle); - theme->clear(); -} - -static jint android_content_AssetManager_loadThemeAttributeValue( - JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve) -{ - ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle); - const ResTable& res(theme->getResTable()); - - Res_value value; - // XXX value could be different in different configs! - uint32_t typeSpecFlags = 0; - ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags); - uint32_t ref = 0; - if (resolve) { - block = res.resolveReference(&value, block, &ref, &typeSpecFlags); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return 0; - } - } + size_t idx = 0; + for (const ResTable_config& configuration : configurations) { + jobject java_configuration = ConstructConfigurationObject(env, configuration); + if (java_configuration == nullptr) { + return nullptr; } - return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block; -} -static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz, - jlong themeHandle) -{ - ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle); - return theme->getChangingConfigurations(); + env->SetObjectArrayElement(array, idx++, java_configuration); + env->DeleteLocalRef(java_configuration); + } + return array; } -static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz, - jlong themeHandle, jint pri, - jstring tag, jstring prefix) -{ - ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle); - const ResTable& res(theme->getResTable()); - (void)res; - - // XXX Need to use params. - theme->dumpToLog(); +static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr, + jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast<Theme*>(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr); + uint32_t* out_values = reinterpret_cast<uint32_t*>(out_values_ptr); + uint32_t* out_indices = reinterpret_cast<uint32_t*>(out_indices_ptr); + + jsize attrs_len = env->GetArrayLength(java_attrs); + jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return; + } + + ApplyStyle(theme, xml_parser, static_cast<uint32_t>(def_style_attr), + static_cast<uint32_t>(def_style_resid), reinterpret_cast<uint32_t*>(attrs), attrs_len, + out_values, out_indices); + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); } -static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz, - jlong themeToken, - jint defStyleAttr, - jint defStyleRes, - jintArray inValues, - jintArray attrs, - jintArray outValues, - jintArray outIndices) -{ - if (themeToken == 0) { - jniThrowNullPointerException(env, "theme token"); - return JNI_FALSE; - } - if (attrs == NULL) { - jniThrowNullPointerException(env, "attrs"); - return JNI_FALSE; - } - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - const jsize NI = env->GetArrayLength(attrs); - const jsize NV = env->GetArrayLength(outValues); - if (NV < (NI*STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); - return JNI_FALSE; - } - - jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); - if (src == NULL) { +static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint def_style_attr, jint def_style_resid, jintArray java_values, + jintArray java_attrs, jintArray out_java_values, + jintArray out_java_indices) { + const jsize attrs_len = env->GetArrayLength(java_attrs); + const jsize out_values_len = env->GetArrayLength(out_java_values); + if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); + return JNI_FALSE; + } + + jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return JNI_FALSE; + } + + jint* values = nullptr; + jsize values_len = 0; + if (java_values != nullptr) { + values_len = env->GetArrayLength(java_values); + values = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_values, nullptr)); + if (values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return JNI_FALSE; + } + } + + jint* out_values = + reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); + if (out_values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + return JNI_FALSE; + } + + jint* out_indices = nullptr; + if (out_java_indices != nullptr) { + jsize out_indices_len = env->GetArrayLength(out_java_indices); + if (out_indices_len > attrs_len) { + out_indices = + reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); + if (out_indices == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); return JNI_FALSE; - } - - jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0); - const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues); + } + } + } + + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast<Theme*>(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + bool result = ResolveAttrs( + theme, static_cast<uint32_t>(def_style_attr), static_cast<uint32_t>(def_style_resid), + reinterpret_cast<uint32_t*>(values), values_len, reinterpret_cast<uint32_t*>(attrs), + attrs_len, reinterpret_cast<uint32_t*>(out_values), reinterpret_cast<uint32_t*>(out_indices)); + if (out_indices != nullptr) { + env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); + } + + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); + if (values != nullptr) { + env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); + } + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return result ? JNI_TRUE : JNI_FALSE; +} - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - if (baseDest == NULL) { - env->ReleasePrimitiveArrayCritical(attrs, src, 0); +static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, + jlong xml_parser_ptr, jintArray java_attrs, + jintArray out_java_values, jintArray out_java_indices) { + const jsize attrs_len = env->GetArrayLength(java_attrs); + const jsize out_values_len = env->GetArrayLength(out_java_values); + if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small"); + return JNI_FALSE; + } + + jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr)); + if (attrs == nullptr) { + return JNI_FALSE; + } + + jint* out_values = + reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_values, nullptr)); + if (out_values == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return JNI_FALSE; + } + + jint* out_indices = nullptr; + if (out_java_indices != nullptr) { + jsize out_indices_len = env->GetArrayLength(out_java_indices); + if (out_indices_len > attrs_len) { + out_indices = + reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_indices, nullptr)); + if (out_indices == nullptr) { + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT); return JNI_FALSE; + } } + } - jint* indices = NULL; - if (outIndices != NULL) { - if (env->GetArrayLength(outIndices) > NI) { - indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); - } - } + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr); - ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken); - bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes, - (uint32_t*) srcValues, NSV, - (uint32_t*) src, NI, - (uint32_t*) baseDest, - (uint32_t*) indices); + bool result = RetrieveAttributes(assetmanager.get(), xml_parser, + reinterpret_cast<uint32_t*>(attrs), attrs_len, + reinterpret_cast<uint32_t*>(out_values), + reinterpret_cast<uint32_t*>(out_indices)); - if (indices != NULL) { - env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); - } - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0); - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return result ? JNI_TRUE : JNI_FALSE; + if (out_indices != nullptr) { + env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); + } + env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); + env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); + return result ? JNI_TRUE : JNI_FALSE; } -static void android_content_AssetManager_applyStyle(JNIEnv* env, jobject, jlong themeToken, - jint defStyleAttr, jint defStyleRes, jlong xmlParserToken, jintArray attrsObj, jint length, - jlong outValuesAddress, jlong outIndicesAddress) { - jint* attrs = env->GetIntArrayElements(attrsObj, 0); - ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken); - ResXMLParser* xmlParser = reinterpret_cast<ResXMLParser*>(xmlParserToken); - uint32_t* outValues = reinterpret_cast<uint32_t*>(static_cast<uintptr_t>(outValuesAddress)); - uint32_t* outIndices = reinterpret_cast<uint32_t*>(static_cast<uintptr_t>(outIndicesAddress)); - ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes, - reinterpret_cast<const uint32_t*>(attrs), length, outValues, outIndices); - env->ReleaseIntArrayElements(attrsObj, attrs, JNI_ABORT); +static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + return reinterpret_cast<jlong>(assetmanager->NewTheme().release()); } -static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz, - jlong xmlParserToken, - jintArray attrs, - jintArray outValues, - jintArray outIndices) -{ - if (xmlParserToken == 0) { - jniThrowNullPointerException(env, "xmlParserToken"); - return JNI_FALSE; - } - if (attrs == NULL) { - jniThrowNullPointerException(env, "attrs"); - return JNI_FALSE; - } - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_FALSE; - } - const ResTable& res(am->getResources()); - ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken; - - const jsize NI = env->GetArrayLength(attrs); - const jsize NV = env->GetArrayLength(outValues); - if (NV < (NI*STYLE_NUM_ENTRIES)) { - jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small"); - return JNI_FALSE; - } - - jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0); - if (src == NULL) { - return JNI_FALSE; - } - - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - if (baseDest == NULL) { - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return JNI_FALSE; - } - - jint* indices = NULL; - if (outIndices != NULL) { - if (env->GetArrayLength(outIndices) > NI) { - indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0); - } - } - - bool result = RetrieveAttributes(&res, xmlParser, - (uint32_t*) src, NI, - (uint32_t*) baseDest, - (uint32_t*) indices); - - if (indices != NULL) { - env->ReleasePrimitiveArrayCritical(outIndices, indices, 0); - } - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - env->ReleasePrimitiveArrayCritical(attrs, src, 0); - return result ? JNI_TRUE : JNI_FALSE; +static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { + delete reinterpret_cast<Theme*>(theme_ptr); } -static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz, - jint id) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - const ResTable& res(am->getResources()); - - res.lock(); - const ResTable::bag_entry* defStyleEnt = NULL; - ssize_t bagOff = res.getBagLocked(id, &defStyleEnt); - res.unlock(); - - return static_cast<jint>(bagOff); +static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint resid, jboolean force) { + // AssetManager is accessed via the theme, so grab an explicit lock here. + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast<Theme*>(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + theme->ApplyStyle(static_cast<uint32_t>(resid), force); + + // TODO(adamlesinski): Consider surfacing exception when result is failure. + // CTS currently expects no exceptions from this method. + // std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid); + // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str()); } -static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz, - jint id, - jintArray outValues) -{ - if (outValues == NULL) { - jniThrowNullPointerException(env, "out values"); - return JNI_FALSE; - } - - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return JNI_FALSE; - } - const ResTable& res(am->getResources()); - ResTable_config config; - Res_value value; - ssize_t block; - - const jsize NV = env->GetArrayLength(outValues); - - jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0); - jint* dest = baseDest; - if (dest == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", ""); - return JNI_FALSE; - } - - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - - const ResTable::bag_entry* arrayEnt = NULL; - uint32_t arrayTypeSetFlags = 0; - ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags); - const ResTable::bag_entry* endArrayEnt = arrayEnt + - (bagOff >= 0 ? bagOff : 0); - - int i = 0; - uint32_t typeSetFlags; - while (i < NV && arrayEnt < endArrayEnt) { - block = arrayEnt->stringBlock; - typeSetFlags = arrayTypeSetFlags; - config.density = 0; - value = arrayEnt->map.value; - - uint32_t resid = 0; - if (value.dataType != Res_value::TYPE_NULL) { - // Take care of resolving the found resource to its final value. - //printf("Resolving attribute reference\n"); - ssize_t newBlock = res.resolveReference(&value, block, &resid, - &typeSetFlags, &config); - if (kThrowOnBadId) { - if (newBlock == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return JNI_FALSE; - } - } - if (newBlock >= 0) block = newBlock; - } - - // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { - value.dataType = Res_value::TYPE_NULL; - value.data = Res_value::DATA_NULL_UNDEFINED; - } - - //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data); - - // Write the final value back to Java. - dest[STYLE_TYPE] = value.dataType; - dest[STYLE_DATA] = value.data; - dest[STYLE_ASSET_COOKIE] = reinterpret_cast<jint>(res.getTableCookie(block)); - dest[STYLE_RESOURCE_ID] = resid; - dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags; - dest[STYLE_DENSITY] = config.density; - dest += STYLE_NUM_ENTRIES; - i+= STYLE_NUM_ENTRIES; - arrayEnt++; - } - - i /= STYLE_NUM_ENTRIES; - - res.unlock(); - - env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0); - - return i; +static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr, + jlong src_theme_ptr) { + Theme* dst_theme = reinterpret_cast<Theme*>(dst_theme_ptr); + Theme* src_theme = reinterpret_cast<Theme*>(src_theme_ptr); + if (!dst_theme->SetTo(*src_theme)) { + jniThrowException(env, "java/lang/IllegalArgumentException", + "Themes are from different AssetManagers"); + } } -static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz, - jint cookie, - jstring fileName) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return 0; - } - - ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz); - - ScopedUtfChars fileName8(env, fileName); - if (fileName8.c_str() == NULL) { - return 0; - } - - int32_t assetCookie = static_cast<int32_t>(cookie); - Asset* a = assetCookie - ? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER) - : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie); - - if (a == NULL) { - jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str()); - return 0; - } - - const DynamicRefTable* dynamicRefTable = - am->getResources().getDynamicRefTableForCookie(assetCookie); - ResXMLTree* block = new ResXMLTree(dynamicRefTable); - status_t err = block->setTo(a->getBuffer(true), a->getLength(), true); - a->close(); - delete a; - - if (err != NO_ERROR) { - jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); - return 0; - } - - return reinterpret_cast<jlong>(block); +static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { + reinterpret_cast<Theme*>(theme_ptr)->Clear(); } -static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N * 2); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - for (size_t i = 0, j = 0; ((ssize_t)i)<N; i++, bag++) { - jint stringIndex = -1; - jint stringBlock = 0; - value = bag->map.value; - - // Take care of resolving the found resource to its final value. - stringBlock = res.resolveReference(&value, bag->stringBlock, NULL); - if (value.dataType == Res_value::TYPE_STRING) { - stringIndex = value.data; - } - - if (kThrowOnBadId) { - if (stringBlock == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - - //todo: It might be faster to allocate a C array to contain - // the blocknums and indices, put them in there and then - // do just one SetIntArrayRegion() - env->SetIntArrayRegion(array, j, 1, &stringBlock); - env->SetIntArrayRegion(array, j + 1, 1, &stringIndex); - j = j + 2; - } - res.unlockBag(startOfBag); - return array; +static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint resid, jobject typed_value, + jboolean resolve_references) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast<Theme*>(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie = theme->GetAttribute(static_cast<uint32_t>(resid), &value, &flags); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + + uint32_t ref = 0u; + if (resolve_references) { + ResTable_config selected_config; + cookie = + theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } + } + return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value); } -static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL); - if (env->ExceptionCheck()) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - size_t strLen = 0; - for (size_t i=0; ((ssize_t)i)<N; i++, bag++) { - value = bag->map.value; - jstring str = NULL; - - // Take care of resolving the found resource to its final value. - ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - if (value.dataType == Res_value::TYPE_STRING) { - const ResStringPool* pool = res.getTableStringBlock(block); - const char* str8 = pool->string8At(value.data, &strLen); - if (str8 != NULL) { - str = env->NewStringUTF(str8); - } else { - const char16_t* str16 = pool->stringAt(value.data, &strLen); - str = env->NewString(reinterpret_cast<const jchar*>(str16), - strLen); - } - - // If one of our NewString{UTF} calls failed due to memory, an - // exception will be pending. - if (env->ExceptionCheck()) { - res.unlockBag(startOfBag); - return NULL; - } - - env->SetObjectArrayElement(array, i, str); - - // str is not NULL at that point, otherwise ExceptionCheck would have been true. - // If we have a large amount of strings in our array, we might - // overflow the local reference table of the VM. - env->DeleteLocalRef(str); - } - } - res.unlockBag(startOfBag); - return array; +static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr, + jint priority, jstring tag, jstring prefix) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + Theme* theme = reinterpret_cast<Theme*>(theme_ptr); + CHECK(theme->GetAssetManager() == &(*assetmanager)); + (void) assetmanager; + (void) theme; + (void) priority; + (void) tag; + (void) prefix; } -static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz, - jint arrayResId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(arrayResId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } - - Res_value value; - const ResTable::bag_entry* bag = startOfBag; - for (size_t i=0; ((ssize_t)i)<N; i++, bag++) { - value = bag->map.value; - - // Take care of resolving the found resource to its final value. - ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL); - if (kThrowOnBadId) { - if (block == BAD_INDEX) { - jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!"); - return array; - } - } - if (value.dataType >= Res_value::TYPE_FIRST_INT - && value.dataType <= Res_value::TYPE_LAST_INT) { - int intVal = value.data; - env->SetIntArrayRegion(array, i, 1, &intVal); - } - } - res.unlockBag(startOfBag); - return array; +static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/, + jlong theme_ptr) { + Theme* theme = reinterpret_cast<Theme*>(theme_ptr); + return static_cast<jint>(theme->GetChangingConfigurations()); } -static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz, - jint styleId) -{ - AssetManager* am = assetManagerForJavaObject(env, clazz); - if (am == NULL) { - return NULL; - } - const ResTable& res(am->getResources()); - - const ResTable::bag_entry* startOfBag; - const ssize_t N = res.lockBag(styleId, &startOfBag); - if (N < 0) { - return NULL; - } - - jintArray array = env->NewIntArray(N); - if (array == NULL) { - res.unlockBag(startOfBag); - return NULL; - } +static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + delete reinterpret_cast<Asset*>(asset_ptr); +} - const ResTable::bag_entry* bag = startOfBag; - for (size_t i=0; ((ssize_t)i)<N; i++, bag++) { - int resourceId = bag->map.name.ident; - env->SetIntArrayRegion(array, i, 1, &resourceId); - } - res.unlockBag(startOfBag); - return array; +static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast<Asset*>(asset_ptr); + uint8_t b; + ssize_t res = asset->read(&b, sizeof(b)); + return res == sizeof(b) ? static_cast<jint>(b) : -1; } -static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem) -{ - if (isSystem) { - verifySystemIdmaps(); - } - AssetManager* am = new AssetManager(); - if (am == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", ""); - return; - } +static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer, + jint offset, jint len) { + if (len == 0) { + return 0; + } - am->addDefaultAssets(); + jsize buffer_len = env->GetArrayLength(java_buffer); + if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len || + offset > buffer_len - len) { + jniThrowException(env, "java/lang/IndexOutOfBoundsException", ""); + return -1; + } - ALOGV("Created AssetManager %p for Java object %p\n", am, clazz); - env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast<jlong>(am)); -} + ScopedByteArrayRW byte_array(env, java_buffer); + if (byte_array.get() == nullptr) { + return -1; + } -static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz) -{ - AssetManager* am = (AssetManager*) - (env->GetLongField(clazz, gAssetManagerOffsets.mObject)); - ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz); - if (am != NULL) { - delete am; - env->SetLongField(clazz, gAssetManagerOffsets.mObject, 0); - } + Asset* asset = reinterpret_cast<Asset*>(asset_ptr); + ssize_t res = asset->read(byte_array.get() + offset, len); + if (res < 0) { + jniThrowException(env, "java/io/IOException", ""); + return -1; + } + return res > 0 ? static_cast<jint>(res) : -1; } -static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz) -{ - return Asset::getGlobalCount(); +static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset, + jint whence) { + Asset* asset = reinterpret_cast<Asset*>(asset_ptr); + return static_cast<jlong>(asset->seek( + static_cast<off64_t>(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR)))); } -static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz) -{ - String8 alloc = Asset::getAssetAllocations(); - if (alloc.length() <= 0) { - return NULL; - } - - jstring str = env->NewStringUTF(alloc.string()); - return str; +static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast<Asset*>(asset_ptr); + return static_cast<jlong>(asset->getLength()); } -static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz) -{ - return AssetManager::getGlobalCount(); +static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) { + Asset* asset = reinterpret_cast<Asset*>(asset_ptr); + return static_cast<jlong>(asset->getRemainingLength()); } // ---------------------------------------------------------------------------- -/* - * JNI registration. - */ +// JNI registration. static const JNINativeMethod gAssetManagerMethods[] = { - /* name, signature, funcPtr */ - - // Basic asset stuff. - { "openAsset", "(Ljava/lang/String;I)J", - (void*) android_content_AssetManager_openAsset }, - { "openAssetFd", "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*) android_content_AssetManager_openAssetFd }, - { "openNonAssetNative", "(ILjava/lang/String;I)J", - (void*) android_content_AssetManager_openNonAssetNative }, - { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*) android_content_AssetManager_openNonAssetFdNative }, - { "list", "(Ljava/lang/String;)[Ljava/lang/String;", - (void*) android_content_AssetManager_list }, - { "destroyAsset", "(J)V", - (void*) android_content_AssetManager_destroyAsset }, - { "readAssetChar", "(J)I", - (void*) android_content_AssetManager_readAssetChar }, - { "readAsset", "(J[BII)I", - (void*) android_content_AssetManager_readAsset }, - { "seekAsset", "(JJI)J", - (void*) android_content_AssetManager_seekAsset }, - { "getAssetLength", "(J)J", - (void*) android_content_AssetManager_getAssetLength }, - { "getAssetRemainingLength", "(J)J", - (void*) android_content_AssetManager_getAssetRemainingLength }, - { "addAssetPathNative", "(Ljava/lang/String;Z)I", - (void*) android_content_AssetManager_addAssetPath }, - { "addAssetFdNative", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)I", - (void*) android_content_AssetManager_addAssetFd }, - { "addOverlayPathNative", "(Ljava/lang/String;)I", - (void*) android_content_AssetManager_addOverlayPath }, - { "isUpToDate", "()Z", - (void*) android_content_AssetManager_isUpToDate }, - - // Resources. - { "getLocales", "()[Ljava/lang/String;", - (void*) android_content_AssetManager_getLocales }, - { "getNonSystemLocales", "()[Ljava/lang/String;", - (void*) android_content_AssetManager_getNonSystemLocales }, - { "getSizeConfigurations", "()[Landroid/content/res/Configuration;", - (void*) android_content_AssetManager_getSizeConfigurations }, - { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIIII)V", - (void*) android_content_AssetManager_setConfiguration }, - { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", - (void*) android_content_AssetManager_getResourceIdentifier }, - { "getResourceName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceName }, - { "getResourcePackageName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourcePackageName }, - { "getResourceTypeName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceTypeName }, - { "getResourceEntryName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getResourceEntryName }, - { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadResourceValue }, - { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadResourceBagValue }, - { "getStringBlockCount","()I", - (void*) android_content_AssetManager_getStringBlockCount }, - { "getNativeStringBlock","(I)J", - (void*) android_content_AssetManager_getNativeStringBlock }, - { "getCookieName","(I)Ljava/lang/String;", - (void*) android_content_AssetManager_getCookieName }, - { "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;", - (void*) android_content_AssetManager_getAssignedPackageIdentifiers }, - - // Themes. - { "newTheme", "()J", - (void*) android_content_AssetManager_newTheme }, - { "deleteTheme", "(J)V", - (void*) android_content_AssetManager_deleteTheme }, - { "applyThemeStyle", "(JIZ)V", - (void*) android_content_AssetManager_applyThemeStyle }, - { "copyTheme", "(JJ)V", - (void*) android_content_AssetManager_copyTheme }, - { "clearTheme", "(J)V", - (void*) android_content_AssetManager_clearTheme }, - { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I", - (void*) android_content_AssetManager_loadThemeAttributeValue }, - { "getThemeChangingConfigurations", "(J)I", - (void*) android_content_AssetManager_getThemeChangingConfigurations }, - { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V", - (void*) android_content_AssetManager_dumpTheme }, - { "applyStyle","(JIIJ[IIJJ)V", - (void*) android_content_AssetManager_applyStyle }, - { "resolveAttrs","(JII[I[I[I[I)Z", - (void*) android_content_AssetManager_resolveAttrs }, - { "retrieveAttributes","(J[I[I[I)Z", - (void*) android_content_AssetManager_retrieveAttributes }, - { "getArraySize","(I)I", - (void*) android_content_AssetManager_getArraySize }, - { "retrieveArray","(I[I)I", - (void*) android_content_AssetManager_retrieveArray }, - - // XML files. - { "openXmlAssetNative", "(ILjava/lang/String;)J", - (void*) android_content_AssetManager_openXmlAssetNative }, - - // Arrays. - { "getArrayStringResource","(I)[Ljava/lang/String;", - (void*) android_content_AssetManager_getArrayStringResource }, - { "getArrayStringInfo","(I)[I", - (void*) android_content_AssetManager_getArrayStringInfo }, - { "getArrayIntResource","(I)[I", - (void*) android_content_AssetManager_getArrayIntResource }, - { "getStyleAttributes","(I)[I", - (void*) android_content_AssetManager_getStyleAttributes }, - - // Bookkeeping. - { "init", "(Z)V", - (void*) android_content_AssetManager_init }, - { "destroy", "()V", - (void*) android_content_AssetManager_destroy }, - { "getGlobalAssetCount", "()I", - (void*) android_content_AssetManager_getGlobalAssetCount }, - { "getAssetAllocations", "()Ljava/lang/String;", - (void*) android_content_AssetManager_getAssetAllocations }, - { "getGlobalAssetManagerCount", "()I", - (void*) android_content_AssetManager_getGlobalAssetManagerCount }, + // AssetManager setup methods. + {"nativeCreate", "()J", (void*)NativeCreate}, + {"nativeDestroy", "(J)V", (void*)NativeDestroy}, + {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets}, + {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V", + (void*)NativeSetConfiguration}, + {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;", + (void*)NativeGetAssignedPackageIdentifiers}, + + // AssetManager file methods. + {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList}, + {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset}, + {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*)NativeOpenAssetFd}, + {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset}, + {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*)NativeOpenNonAssetFd}, + {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset}, + + // AssetManager resource methods. + {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue}, + {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I", + (void*)NativeGetResourceBagValue}, + {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes}, + {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;", + (void*)NativeGetResourceStringArray}, + {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo}, + {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray}, + {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize}, + {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray}, + + // AssetManager resource name/ID methods. + {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + (void*)NativeGetResourceIdentifier}, + {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName}, + {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName}, + {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName}, + {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName}, + {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales}, + {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;", + (void*)NativeGetSizeConfigurations}, + + // Style attribute related methods. + {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle}, + {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs}, + {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes}, + + // Theme related methods. + {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate}, + {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy}, + {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle}, + {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy}, + {"nativeThemeClear", "(J)V", (void*)NativeThemeClear}, + {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I", + (void*)NativeThemeGetAttributeValue}, + {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump}, + {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations}, + + // AssetInputStream methods. + {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy}, + {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar}, + {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead}, + {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek}, + {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength}, + {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength}, + + // System/idmap related methods. + {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps}, + + // Global management/debug methods. + {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount}, + {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations}, + {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount}, }; -int register_android_content_AssetManager(JNIEnv* env) -{ - jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); - gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); - gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); - gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string", - "Ljava/lang/CharSequence;"); - gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); - gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); - gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue, - "changingConfigurations", "I"); - gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); - - jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); - gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd", - "Landroid/os/ParcelFileDescriptor;"); - gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); - gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); - - jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); - gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); - - jclass stringClass = FindClassOrDie(env, "java/lang/String"); - g_stringClass = MakeGlobalRefOrDie(env, stringClass); - - jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); - gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); - gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, - "<init>", "()V"); - gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", - "(ILjava/lang/Object;)V"); - - jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); - gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); - gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, - "<init>", "()V"); - gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, - "smallestScreenWidthDp", "I"); - gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass, - "screenWidthDp", "I"); - gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass, - "screenHeightDp", "I"); - - return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, - NELEM(gAssetManagerMethods)); +int register_android_content_AssetManager(JNIEnv* env) { + jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets"); + gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J"); + + jclass typedValue = FindClassOrDie(env, "android/util/TypedValue"); + gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I"); + gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I"); + gTypedValueOffsets.mString = + GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;"); + gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I"); + gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I"); + gTypedValueOffsets.mChangingConfigurations = + GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I"); + gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I"); + + jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor"); + gAssetFileDescriptorOffsets.mFd = + GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;"); + gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J"); + gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J"); + + jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager"); + gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J"); + + jclass stringClass = FindClassOrDie(env, "java/lang/String"); + g_stringClass = MakeGlobalRefOrDie(env, stringClass); + + jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray"); + gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass); + gSparseArrayOffsets.constructor = + GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "<init>", "()V"); + gSparseArrayOffsets.put = + GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V"); + + jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration"); + gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass); + gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "<init>", "()V"); + gConfigurationOffsets.mSmallestScreenWidthDpOffset = + GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I"); + gConfigurationOffsets.mScreenWidthDpOffset = + GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I"); + gConfigurationOffsets.mScreenHeightDpOffset = + GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I"); + + return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods, + NELEM(gAssetManagerMethods)); } }; // namespace android diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 13e0e4a6bdb7..b614c891b9f8 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -984,6 +984,10 @@ static void android_view_ThreadedRenderer_hackySetRTAnimationsEnabled(JNIEnv*, j Properties::enableRTAnimations = enable; } +static void android_view_ThreadedRenderer_setDebuggingEnabled(JNIEnv*, jclass, jboolean enable) { + Properties::debuggingEnabled = enable; +} + // ---------------------------------------------------------------------------- // FrameMetricsObserver // ---------------------------------------------------------------------------- @@ -1092,6 +1096,7 @@ static const JNINativeMethod gMethods[] = { { "nSetHighContrastText", "(Z)V", (void*)android_view_ThreadedRenderer_setHighContrastText }, { "nHackySetRTAnimationsEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_hackySetRTAnimationsEnabled }, + { "nSetDebuggingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDebuggingEnabled }, }; static JavaVM* mJvm = nullptr; diff --git a/core/jni/include/android_runtime/android_util_AssetManager.h b/core/jni/include/android_runtime/android_util_AssetManager.h index 8dd933707a6a..2c1e3579eb92 100644 --- a/core/jni/include/android_runtime/android_util_AssetManager.h +++ b/core/jni/include/android_runtime/android_util_AssetManager.h @@ -14,17 +14,20 @@ * limitations under the License. */ -#ifndef android_util_AssetManager_H -#define android_util_AssetManager_H +#ifndef ANDROID_RUNTIME_ASSETMANAGER_H +#define ANDROID_RUNTIME_ASSETMANAGER_H -#include <androidfw/AssetManager.h> +#include "androidfw/AssetManager2.h" +#include "androidfw/MutexGuard.h" #include "jni.h" namespace android { -extern AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject assetMgr); +extern AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); +extern Guarded<AssetManager2>* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager); +extern Guarded<AssetManager2>* AssetManagerForNdkAssetManager(AAssetManager* assetmanager); -} +} // namespace android -#endif +#endif // ANDROID_RUNTIME_ASSETMANAGER_H diff --git a/core/proto/android/server/statlogger.proto b/core/proto/android/server/statlogger.proto index 2ae526a59966..2b7daea00a6c 100644 --- a/core/proto/android/server/statlogger.proto +++ b/core/proto/android/server/statlogger.proto @@ -24,10 +24,13 @@ import "frameworks/base/libs/incident/proto/android/privacy.proto"; // Dump from StatLogger. message StatLoggerProto { - option (.android.msg_privacy).dest = DEST_EXPLICIT; + option (.android.msg_privacy).dest = DEST_AUTOMATIC; message Event { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 eventId = 1; + // Labels are hard-coded in Android framework code. optional string label = 2; optional int32 count = 3; optional int64 total_duration_micros = 4; diff --git a/core/res/res/drawable-watch/global_action_icon_background.xml b/core/res/res/drawable-watch/global_action_icon_background.xml new file mode 100644 index 000000000000..b7bc7e6ce402 --- /dev/null +++ b/core/res/res/drawable-watch/global_action_icon_background.xml @@ -0,0 +1,21 @@ +<?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" + android:shape="oval" + android:tint="?android:attr/colorButtonNormal"> + <solid android:color="@android:color/white"/> + <size android:height="40dp" android:width="40dp"/> +</shape> diff --git a/core/res/res/drawable/ic_qs_battery_saver.xml b/core/res/res/drawable/ic_qs_battery_saver.xml new file mode 100644 index 000000000000..89b25690a5db --- /dev/null +++ b/core/res/res/drawable/ic_qs_battery_saver.xml @@ -0,0 +1,28 @@ +<!-- + Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:autoMirrored="true" + android:width="32.0dp" + android:height="32.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?android:attr/colorControlNormal"> + <path + android:pathData="M5,3 + l3.5,0 l0,-1.5 l7,0 l0,1.5 l3.5,0 l0,19.5 l-14,0z + M10.5,8.5 l0,3 l-3,0 l0,3 l3,0 l0,3 l3,0 l0,-3 l3,0 l0,-3 l-3,0 l0,-3 z" + android:fillColor="#FFFFFF"/> +</vector> diff --git a/core/res/res/layout-watch/global_actions_item.xml b/core/res/res/layout-watch/global_actions_item.xml new file mode 100644 index 000000000000..025d45e84dbb --- /dev/null +++ b/core/res/res/layout-watch/global_actions_item.xml @@ -0,0 +1,58 @@ +<?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:minHeight="?attr/listPreferredItemHeightSmall" + android:gravity="center_vertical" + android:paddingStart="?attr/listPreferredItemPaddingStart" + android:paddingEnd="?attr/listPreferredItemPaddingEnd" + android:background="?attr/activatedBackgroundIndicator" + android:clipToPadding="false"> + <ImageView android:id="@+id/icon" + android:background="@drawable/global_action_icon_background" + android:scaleType="centerInside" + android:layout_width="40dp" + android:layout_height="40dp" + android:padding="8dp" + android:gravity="center" + android:orientation="horizontal" + android:layout_marginEnd="8dp" + android:layout_marginTop="8dp" + android:layout_marginBottom="8dp"/> + + <RelativeLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:layout_marginTop="8dp" + android:layout_marginBottom="8dp"> + + <TextView android:id="@+id/message" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?attr/textAppearanceListItem" + android:ellipsize="end" /> + + <TextView android:id="@+id/status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/message" + android:layout_alignStart="@id/message" + android:textAppearance="?attr/textAppearanceListItemSecondary" + android:textColor="?attr/textColorSecondary"/> + </RelativeLayout> +</LinearLayout> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index f3aa054b50b6..375627d806df 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2163,6 +2163,9 @@ <!-- Whether UI for multi user should be shown --> <bool name="config_enableMultiUserUI">false</bool> + <!-- Whether the new Auto Selection Network UI should be shown --> + <bool name="config_enableNewAutoSelectNetworkUI">false</bool> + <!-- If true, then we do not ask user for permission for apps to connect to USB devices. Do not set this to true for production devices. Doing so will cause you to fail CTS. --> <bool name="config_disableUsbPermissionDialogs">false</bool> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index f38dcea284e5..d59ba1822f47 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -539,6 +539,15 @@ <!-- status message in phone options dialog for when airplane mode is off --> <string name="global_actions_airplane_mode_off_status">Airplane mode is OFF</string> + <!-- label for item that enables battery saver in phone options dialog --> + <string name="global_action_toggle_battery_saver">Battery saver</string> + + <!-- status message in phone options dialog for when battery saver is enabled --> + <string name="global_action_battery_saver_on_status">Battery saver is OFF</string> + + <!-- status message in phone options dialog for when battery saver is disabled --> + <string name="global_action_battery_saver_off_status">Battery saver is ON</string> + <!-- label for item that launches settings in phone options dialog [CHAR LIMIT=15]--> <string name="global_action_settings">Settings</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 3b96861e45fc..0cd100797325 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -319,6 +319,7 @@ <java-symbol type="bool" name="config_wifi_softap_acs_supported" /> <java-symbol type="bool" name="config_wifi_softap_ieee80211ac_supported" /> <java-symbol type="bool" name="config_enableMultiUserUI"/> + <java-symbol type="bool" name="config_enableNewAutoSelectNetworkUI"/> <java-symbol type="bool" name="config_disableUsbPermissionDialogs"/> <java-symbol type="bool" name="config_hasRecents" /> <java-symbol type="string" name="config_recentsComponentName" /> @@ -1377,6 +1378,7 @@ <java-symbol type="drawable" name="ic_corp_statusbar_icon" /> <java-symbol type="drawable" name="ic_instant_icon_badge_bolt" /> <java-symbol type="drawable" name="emulator_circular_window_overlay" /> + <java-symbol type="drawable" name="ic_qs_battery_saver" /> <java-symbol type="drawable" name="sim_light_blue" /> <java-symbol type="drawable" name="sim_light_green" /> @@ -1737,6 +1739,9 @@ <java-symbol type="string" name="global_action_silent_mode_off_status" /> <java-symbol type="string" name="global_action_silent_mode_on_status" /> <java-symbol type="string" name="global_action_toggle_silent_mode" /> + <java-symbol type="string" name="global_action_battery_saver_off_status" /> + <java-symbol type="string" name="global_action_battery_saver_on_status" /> + <java-symbol type="string" name="global_action_toggle_battery_saver" /> <java-symbol type="string" name="global_action_lockdown" /> <java-symbol type="string" name="global_action_voice_assist" /> <java-symbol type="string" name="global_action_assist" /> diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml index d80c6972db1c..bd0958dcf6c1 100644 --- a/core/res/res/xml/power_profile.xml +++ b/core/res/res/xml/power_profile.xml @@ -28,8 +28,9 @@ significantly, so should be measured on the shipping platform with a power meter. --> <item name="none">0</item> - <item name="screen.on">0.1</item> <!-- ~200mA --> - <item name="screen.full">0.1</item> <!-- ~300mA --> + <item name="ambient.on">0.1</item> <!-- ~100mA --> + <item name="screen.on">0.1</item> <!-- ~100mA --> + <item name="screen.full">0.1</item> <!-- ~100mA --> <item name="bluetooth.active">0.1</item> <!-- Bluetooth data transfer, ~10mA --> <item name="bluetooth.on">0.1</item> <!-- Bluetooth on & connectable, but not connected, ~0.1mA --> <item name="wifi.on">0.1</item> <!-- ~3mA --> diff --git a/core/res/res/xml/power_profile_test.xml b/core/res/res/xml/power_profile_test.xml index cdb71343e5e5..eb347325a5b6 100644 --- a/core/res/res/xml/power_profile_test.xml +++ b/core/res/res/xml/power_profile_test.xml @@ -80,6 +80,8 @@ <value>60</value> <!-- 3000 MHz CPU speed --> </array> + <!-- Power used by display unit in ambient display mode, including back lighting--> + <item name="ambient.on">0.5</item> <!-- Additional power used when screen is turned on at minimum brightness --> <item name="screen.on">100</item> <!-- Additional power used when screen is at maximum brightness, compared to diff --git a/core/tests/coretests/src/android/content/BroadcastReceiverTests.java b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java new file mode 100644 index 000000000000..8deccb7ffa7f --- /dev/null +++ b/core/tests/coretests/src/android/content/BroadcastReceiverTests.java @@ -0,0 +1,60 @@ +/* + * 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.content; + +import static org.junit.Assert.fail; + +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class BroadcastReceiverTests { + + private static final int RECEIVER_LIMIT_PER_APP = 1000; + private static final class EmptyReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + // Empty + } + } + private Context mContext; + + @Before + public void setUp() { + mContext = InstrumentationRegistry.getTargetContext(); + } + + @Test + public void testReceiverLimit() { + final IntentFilter mockFilter = new IntentFilter("android.content.tests.TestAction"); + try { + for (int i = 0; i < RECEIVER_LIMIT_PER_APP + 1; i++) { + mContext.registerReceiver(new EmptyReceiver(), mockFilter); + } + fail("No exception thrown when registering " + + (RECEIVER_LIMIT_PER_APP + 1) + " receivers"); + } catch (IllegalStateException ise) { + // Expected + } + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java index eb7da9ca4ffc..c7de99a0ade0 100644 --- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java +++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java @@ -53,6 +53,7 @@ public class PowerProfileTest extends TestCase { assertEquals(4, mProfile.getNumSpeedStepsInCpuCluster(1)); assertEquals(60.0, mProfile.getAveragePowerForCpuCore(1, 3)); assertEquals(3000.0, mProfile.getBatteryCapacity()); + assertEquals(0.5, mProfile.getAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY)); } } diff --git a/core/tests/coretests/src/com/android/internal/textservice/LazyIntToIntMapTest.java b/core/tests/coretests/src/com/android/internal/textservice/LazyIntToIntMapTest.java new file mode 100644 index 000000000000..351852710773 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/textservice/LazyIntToIntMapTest.java @@ -0,0 +1,92 @@ +/* + * 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.internal.textservice; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.IntUnaryOperator; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class LazyIntToIntMapTest { + @Test + public void testLaziness() { + final IntUnaryOperator func = mock(IntUnaryOperator.class); + when(func.applyAsInt(eq(1))).thenReturn(11); + when(func.applyAsInt(eq(2))).thenReturn(22); + + final LazyIntToIntMap map = new LazyIntToIntMap(func); + + verify(func, never()).applyAsInt(anyInt()); + + assertEquals(22, map.get(2)); + verify(func, times(0)).applyAsInt(eq(1)); + verify(func, times(1)).applyAsInt(eq(2)); + + // Accessing to the same key does not evaluate the function again. + assertEquals(22, map.get(2)); + verify(func, times(0)).applyAsInt(eq(1)); + verify(func, times(1)).applyAsInt(eq(2)); + } + + @Test + public void testDelete() { + final IntUnaryOperator func1 = mock(IntUnaryOperator.class); + when(func1.applyAsInt(eq(1))).thenReturn(11); + when(func1.applyAsInt(eq(2))).thenReturn(22); + + final IntUnaryOperator func2 = mock(IntUnaryOperator.class); + when(func2.applyAsInt(eq(1))).thenReturn(111); + when(func2.applyAsInt(eq(2))).thenReturn(222); + + final AtomicReference<IntUnaryOperator> funcRef = new AtomicReference<>(func1); + final LazyIntToIntMap map = new LazyIntToIntMap(i -> funcRef.get().applyAsInt(i)); + + verify(func1, never()).applyAsInt(anyInt()); + verify(func2, never()).applyAsInt(anyInt()); + + assertEquals(22, map.get(2)); + verify(func1, times(1)).applyAsInt(eq(2)); + verify(func2, times(0)).applyAsInt(eq(2)); + + // Swap func1 with func2 then invalidate the key=2 + funcRef.set(func2); + map.delete(2); + + // Calling get(2) again should re-evaluate the value. + assertEquals(222, map.get(2)); + verify(func1, times(1)).applyAsInt(eq(2)); + verify(func2, times(1)).applyAsInt(eq(2)); + + // Trying to delete non-existing keys does nothing. + map.delete(1); + } +} diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 251b2e773cfb..70d52164ff74 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -145,6 +145,7 @@ cc_test { "tests/TypeWrappers_test.cpp", "tests/ZipUtils_test.cpp", ], + static_libs: ["libgmock"], target: { android: { srcs: [ @@ -171,6 +172,7 @@ cc_benchmark { // Actual benchmarks. "tests/AssetManager2_bench.cpp", + "tests/AttributeResolution_bench.cpp", "tests/SparseEntry_bench.cpp", "tests/Theme_bench.cpp", ], diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index da0205d72125..8f58f74d4652 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define ATRACE_TAG ATRACE_TAG_RESOURCES - #include "androidfw/ApkAssets.h" #include <algorithm> @@ -27,7 +25,6 @@ #include "android-base/utf8.h" #include "utils/Compat.h" #include "utils/FileMap.h" -#include "utils/Trace.h" #include "ziparchive/zip_archive.h" #include "androidfw/Asset.h" @@ -105,8 +102,6 @@ std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) { std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl( unique_fd fd, const std::string& path, std::unique_ptr<Asset> idmap_asset, std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library) { - ATRACE_CALL(); - ::ZipArchiveHandle unmanaged_handle; int32_t result; if (fd >= 0) { @@ -163,7 +158,6 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl( } std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const { - ATRACE_CALL(); CHECK(zip_handle_ != nullptr); ::ZipString name(path.c_str()); @@ -231,12 +225,16 @@ bool ApkAssets::ForEachFile(const std::string& root_path, while ((result = ::Next(cookie, &entry, &name)) == 0) { StringPiece full_file_path(reinterpret_cast<const char*>(name.name), name.name_length); StringPiece leaf_file_path = full_file_path.substr(root_path_full.size()); - auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/'); - if (iter != leaf_file_path.end()) { - dirs.insert( - leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string()); - } else if (!leaf_file_path.empty()) { - f(leaf_file_path, kFileTypeRegular); + + if (!leaf_file_path.empty()) { + auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/'); + if (iter != leaf_file_path.end()) { + std::string dir = + leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string(); + dirs.insert(std::move(dir)); + } else { + f(leaf_file_path, kFileTypeRegular); + } } } ::EndIteration(cookie); diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 415d3e36adf9..d9f1293183b7 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -36,6 +36,31 @@ namespace android { +struct FindEntryResult { + // A pointer to the resource table entry for this resource. + // If the size of the entry is > sizeof(ResTable_entry), it can be cast to + // a ResTable_map_entry and processed as a bag/map. + const ResTable_entry* entry; + + // The configuration for which the resulting entry was defined. This is already swapped to host + // endianness. + ResTable_config config; + + // The bitmask of configuration axis with which the resource value varies. + uint32_t type_flags; + + // The dynamic package ID map for the package from which this resource came from. + const DynamicRefTable* dynamic_ref_table; + + // The string pool reference to the type's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef type_string_ref; + + // The string pool reference to the entry's name. This uses a different string pool than + // the global string pool, but this is hidden from the caller. + StringPoolRef entry_string_ref; +}; + AssetManager2::AssetManager2() { memset(&configuration_, 0, sizeof(configuration_)); } @@ -44,6 +69,7 @@ bool AssetManager2::SetApkAssets(const std::vector<const ApkAssets*>& apk_assets bool invalidate_caches) { apk_assets_ = apk_assets; BuildDynamicRefTable(); + RebuildFilterList(); if (invalidate_caches) { InvalidateCaches(static_cast<uint32_t>(-1)); } @@ -74,12 +100,14 @@ void AssetManager2::BuildDynamicRefTable() { if (idx == 0xff) { package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size()); package_groups_.push_back({}); - package_groups_.back().dynamic_ref_table.mAssignedPackageId = package_id; + DynamicRefTable& ref_table = package_groups_.back().dynamic_ref_table; + ref_table.mAssignedPackageId = package_id; + ref_table.mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f; } PackageGroup* package_group = &package_groups_[idx]; // Add the package and to the set of packages with the same ID. - package_group->packages_.push_back(package.get()); + package_group->packages_.push_back(ConfiguredPackage{package.get(), {}}); package_group->cookies_.push_back(static_cast<ApkAssetsCookie>(i)); // Add the package name -> build time ID mappings. @@ -94,7 +122,7 @@ void AssetManager2::BuildDynamicRefTable() { // Now assign the runtime IDs so that we have a build-time to runtime ID map. const auto package_groups_end = package_groups_.end(); for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) { - const std::string& package_name = iter->packages_[0]->GetPackageName(); + const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName(); for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) { iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()), iter->dynamic_ref_table.mAssignedPackageId); @@ -105,20 +133,33 @@ void AssetManager2::BuildDynamicRefTable() { void AssetManager2::DumpToLog() const { base::ScopedLogSeverity _log(base::INFO); + LOG(INFO) << base::StringPrintf("AssetManager2(this=%p)", this); + std::string list; + for (const auto& apk_assets : apk_assets_) { + base::StringAppendF(&list, "%s,", apk_assets->GetPath().c_str()); + } + LOG(INFO) << "ApkAssets: " << list; + + list = ""; for (size_t i = 0; i < package_ids_.size(); i++) { if (package_ids_[i] != 0xff) { - base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]); + base::StringAppendF(&list, "%02x -> %d, ", (int)i, package_ids_[i]); } } LOG(INFO) << "Package ID map: " << list; for (const auto& package_group: package_groups_) { - list = ""; - for (const auto& package : package_group.packages_) { - base::StringAppendF(&list, "%s(%02x), ", package->GetPackageName().c_str(), package->GetPackageId()); - } - LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list; + list = ""; + for (const auto& package : package_group.packages_) { + const LoadedPackage* loaded_package = package.loaded_package_; + base::StringAppendF(&list, "%s(%02x%s), ", loaded_package->GetPackageName().c_str(), + loaded_package->GetPackageId(), + (loaded_package->IsDynamic() ? " dynamic" : "")); + } + LOG(INFO) << base::StringPrintf("PG (%02x): ", + package_group.dynamic_ref_table.mAssignedPackageId) + << list; } } @@ -157,53 +198,55 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { configuration_ = configuration; if (diff) { + RebuildFilterList(); InvalidateCaches(static_cast<uint32_t>(diff)); } } std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system, - bool exclude_mipmap) { - ATRACE_CALL(); + bool exclude_mipmap) const { + ATRACE_NAME("AssetManager::GetResourceConfigurations"); std::set<ResTable_config> configurations; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { - if (exclude_system && package->IsSystem()) { + for (const ConfiguredPackage& package : package_group.packages_) { + if (exclude_system && package.loaded_package_->IsSystem()) { continue; } - package->CollectConfigurations(exclude_mipmap, &configurations); + package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations); } } return configurations; } std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system, - bool merge_equivalent_languages) { - ATRACE_CALL(); + bool merge_equivalent_languages) const { + ATRACE_NAME("AssetManager::GetResourceLocales"); std::set<std::string> locales; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { - if (exclude_system && package->IsSystem()) { + for (const ConfiguredPackage& package : package_group.packages_) { + if (exclude_system && package.loaded_package_->IsSystem()) { continue; } - package->CollectLocales(merge_equivalent_languages, &locales); + package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales); } } return locales; } -std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) { +std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, + Asset::AccessMode mode) const { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, mode); } std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode) { + Asset::AccessMode mode) const { const std::string new_path = "assets/" + filename; return OpenNonAsset(new_path, cookie, mode); } -std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) { - ATRACE_CALL(); +std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) const { + ATRACE_NAME("AssetManager::OpenDir"); std::string full_path = "assets/" + dirname; std::unique_ptr<SortedVector<AssetDir::FileInfo>> files = @@ -236,8 +279,7 @@ std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) { // is inconsistent for split APKs. std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie) { - ATRACE_CALL(); + ApkAssetsCookie* out_cookie) const { for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) { std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode); if (asset) { @@ -255,8 +297,8 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, } std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, - ApkAssetsCookie cookie, Asset::AccessMode mode) { - ATRACE_CALL(); + ApkAssetsCookie cookie, + Asset::AccessMode mode) const { if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) { return {}; } @@ -264,14 +306,13 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, } ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, - bool stop_at_first_match, FindEntryResult* out_entry) { - ATRACE_CALL(); - + bool /*stop_at_first_match*/, + FindEntryResult* out_entry) const { // Might use this if density_override != 0. ResTable_config density_override_config; // Select our configuration or generate a density override configuration. - ResTable_config* desired_config = &configuration_; + const ResTable_config* desired_config = &configuration_; if (density_override != 0 && density_override != configuration_.density) { density_override_config = configuration_; density_override_config.density = density_override; @@ -285,55 +326,135 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri const uint32_t package_id = get_package_id(resid); const uint8_t type_idx = get_type_id(resid) - 1; - const uint16_t entry_id = get_entry_id(resid); + const uint16_t entry_idx = get_entry_id(resid); - const uint8_t idx = package_ids_[package_id]; - if (idx == 0xff) { + const uint8_t package_idx = package_ids_[package_id]; + if (package_idx == 0xff) { LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); return kInvalidCookie; } - FindEntryResult best_entry; - ApkAssetsCookie best_cookie = kInvalidCookie; - uint32_t cumulated_flags = 0u; - - const PackageGroup& package_group = package_groups_[idx]; + const PackageGroup& package_group = package_groups_[package_idx]; const size_t package_count = package_group.packages_.size(); - FindEntryResult current_entry; - for (size_t i = 0; i < package_count; i++) { - const LoadedPackage* loaded_package = package_group.packages_[i]; - if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry)) { + + ApkAssetsCookie best_cookie = kInvalidCookie; + const LoadedPackage* best_package = nullptr; + const ResTable_type* best_type = nullptr; + const ResTable_config* best_config = nullptr; + ResTable_config best_config_copy; + uint32_t best_offset = 0u; + uint32_t type_flags = 0u; + + // If desired_config is the same as the set configuration, then we can use our filtered list + // and we don't need to match the configurations, since they already matched. + const bool use_fast_path = desired_config == &configuration_; + + for (size_t pi = 0; pi < package_count; pi++) { + const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; + const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_; + ApkAssetsCookie cookie = package_group.cookies_[pi]; + + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx); + if (UNLIKELY(type_spec == nullptr)) { continue; } - cumulated_flags |= current_entry.type_flags; + uint16_t local_entry_idx = entry_idx; - const ResTable_config* current_config = current_entry.config; - const ResTable_config* best_config = best_entry.config; - if (best_cookie == kInvalidCookie || - current_config->isBetterThan(*best_config, desired_config) || - (loaded_package->IsOverlay() && current_config->compare(*best_config) == 0)) { - best_entry = current_entry; - best_cookie = package_group.cookies_[i]; - if (stop_at_first_match) { - break; + // If there is an IDMAP supplied with this package, translate the entry ID. + if (type_spec->idmap_entries != nullptr) { + if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) { + // There is no mapping, so the resource is not meant to be in this overlay package. + continue; } } + + type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx); + + // If the package is an overlay, then even configurations that are the same MUST be chosen. + const bool package_is_overlay = loaded_package->IsOverlay(); + + const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; + if (use_fast_path) { + const std::vector<ResTable_config>& candidate_configs = filtered_group.configurations; + const size_t type_count = candidate_configs.size(); + for (uint32_t i = 0; i < type_count; i++) { + const ResTable_config& this_config = candidate_configs[i]; + + // We can skip calling ResTable_config::match() because we know that all candidate + // configurations that do NOT match have been filtered-out. + if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || + (package_is_overlay && this_config.compare(*best_config) == 0)) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const ResTable_type* type_chunk = filtered_group.types[i]; + const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + best_cookie = cookie; + best_package = loaded_package; + best_type = type_chunk; + best_config = &this_config; + best_offset = offset; + } + } + } else { + // This is the slower path, which doesn't use the filtered list of configurations. + // Here we must read the ResTable_config from the mmapped APK, convert it to host endianness + // and fill in any new fields that did not exist when the APK was compiled. + // Furthermore when selecting configurations we can't just record the pointer to the + // ResTable_config, we must copy it. + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config this_config; + this_config.copyFromDtoH((*iter)->config); + + if (this_config.match(*desired_config)) { + if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || + (package_is_overlay && this_config.compare(*best_config) == 0)) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + best_cookie = cookie; + best_package = loaded_package; + best_type = *iter; + best_config_copy = this_config; + best_config = &best_config_copy; + best_offset = offset; + } + } + } + } + } + + if (UNLIKELY(best_cookie == kInvalidCookie)) { + return kInvalidCookie; } - if (best_cookie == kInvalidCookie) { + const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset); + if (UNLIKELY(best_entry == nullptr)) { return kInvalidCookie; } - *out_entry = best_entry; + out_entry->entry = best_entry; + out_entry->config = *best_config; + out_entry->type_flags = type_flags; + out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); + out_entry->entry_string_ref = + StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; - out_entry->type_flags = cumulated_flags; return best_cookie; } -bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { - ATRACE_CALL(); - +bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const { FindEntryResult entry; ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */, &entry); @@ -341,7 +462,8 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { return false; } - const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid); + const LoadedPackage* package = + apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); if (package == nullptr) { return false; } @@ -369,7 +491,7 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) { return true; } -bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { +bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const { FindEntryResult entry; ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); @@ -383,9 +505,7 @@ bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) { ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags) { - ATRACE_CALL(); - + uint32_t* out_flags) const { FindEntryResult entry; ApkAssetsCookie cookie = FindEntry(resid, density_override, false /* stop_at_first_match */, &entry); @@ -402,7 +522,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Create a reference since we can't represent this complex type as a Res_value. out_value->dataType = Res_value::TYPE_REFERENCE; out_value->data = resid; - *out_selected_config = *entry.config; + *out_selected_config = entry.config; *out_flags = entry.type_flags; return cookie; } @@ -414,7 +534,7 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, // Convert the package ID to the runtime assigned package ID. entry.dynamic_ref_table->lookupResourceValue(out_value); - *out_selected_config = *entry.config; + *out_selected_config = entry.config; *out_flags = entry.type_flags; return cookie; } @@ -422,16 +542,13 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference) { - ATRACE_CALL(); + uint32_t* out_last_reference) const { constexpr const int kMaxIterations = 20; for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && in_out_value->data != 0u && iteration < kMaxIterations; iteration++) { - if (out_last_reference != nullptr) { - *out_last_reference = in_out_value->data; - } + *out_last_reference = in_out_value->data; uint32_t new_flags = 0u; cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/, in_out_value, in_out_selected_config, &new_flags); @@ -450,7 +567,7 @@ ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_valu } const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { - ATRACE_CALL(); + ATRACE_NAME("AssetManager::GetBag"); auto cached_iter = cached_bags_.find(resid); if (cached_iter != cached_bags_.end()) { @@ -492,7 +609,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { // Attributes, arrays, etc don't have a resource id as the name. They specify // other data, which would be wrong to change via a lookup. if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, + resid); return nullptr; } } @@ -524,7 +642,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { const ResolvedBag* parent_bag = GetBag(parent_resid); if (parent_bag == nullptr) { // Failed to get the parent that should exist. - LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid); + LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, + resid); return nullptr; } @@ -543,7 +662,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t child_key = dtohl(map_entry->name.ident); if (!is_internal_resid(child_key)) { if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, + resid); return nullptr; } } @@ -582,7 +702,8 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { uint32_t new_key = dtohl(map_entry->name.ident); if (!is_internal_resid(new_key)) { if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { - LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); + LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, + resid); return nullptr; } } @@ -638,7 +759,7 @@ static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) { uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const std::string& fallback_type, - const std::string& fallback_package) { + const std::string& fallback_package) const { StringPiece package_name, type, entry; if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) { return 0u; @@ -670,7 +791,8 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, const static std::u16string kAttrPrivate16 = u"^attr-private"; for (const PackageGroup& package_group : package_groups_) { - for (const LoadedPackage* package : package_group.packages_) { + for (const ConfiguredPackage& package_impl : package_group.packages_) { + const LoadedPackage* package = package_impl.loaded_package_; if (package_name != package->GetPackageName()) { // All packages in the same group are expected to have the same package name. break; @@ -692,6 +814,32 @@ uint32_t AssetManager2::GetResourceId(const std::string& resource_name, return 0u; } +void AssetManager2::RebuildFilterList() { + for (PackageGroup& group : package_groups_) { + for (ConfiguredPackage& impl : group.packages_) { + // Destroy it. + impl.filtered_configs_.~ByteBucketArray(); + + // Re-create it. + new (&impl.filtered_configs_) ByteBucketArray<FilteredConfigGroup>(); + + // Create the filters here. + impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec* spec, uint8_t type_index) { + FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_index); + const auto iter_end = spec->types + spec->type_count; + for (auto iter = spec->types; iter != iter_end; ++iter) { + ResTable_config this_config; + this_config.copyFromDtoH((*iter)->config); + if (this_config.match(configuration_)) { + group.configurations.push_back(this_config); + group.types.push_back(*iter); + } + } + }); + } + } +} + void AssetManager2::InvalidateCaches(uint32_t diff) { if (diff == 0xffffffffu) { // Everything must go. @@ -743,7 +891,7 @@ struct Theme::Package { }; bool Theme::ApplyStyle(uint32_t resid, bool force) { - ATRACE_CALL(); + ATRACE_NAME("Theme::ApplyStyle"); const ResolvedBag* bag = asset_manager_->GetBag(resid); if (bag == nullptr) { @@ -872,7 +1020,7 @@ ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_type_spec_flags, - uint32_t* out_last_ref) { + uint32_t* out_last_ref) const { if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) { uint32_t new_flags; cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags); diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index 60e3845d98a9..f912af4f7190 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -20,13 +20,18 @@ #include <log/log.h> +#include "androidfw/AssetManager2.h" #include "androidfw/AttributeFinder.h" -#include "androidfw/ResourceTypes.h" constexpr bool kDebugStyles = false; namespace android { +// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. +static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { + return cookie != kInvalidCookie ? static_cast<uint32_t>(cookie + 1) : static_cast<uint32_t>(-1); +} + class XmlAttributeFinder : public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> { public: @@ -44,58 +49,53 @@ class XmlAttributeFinder }; class BagAttributeFinder - : public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> { + : public BackTrackingAttributeFinder<BagAttributeFinder, const ResolvedBag::Entry*> { public: - BagAttributeFinder(const ResTable::bag_entry* start, - const ResTable::bag_entry* end) - : BackTrackingAttributeFinder(start, end) {} + BagAttributeFinder(const ResolvedBag* bag) + : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr, + bag != nullptr ? bag->entries + bag->entry_count : nullptr) { + } - inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const { - return entry->map.name.ident; + inline uint32_t GetAttribute(const ResolvedBag::Entry* entry) const { + return entry->key; } }; -bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, - uint32_t def_style_res, uint32_t* src_values, - size_t src_values_length, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, - uint32_t* out_indices) { +bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, + uint32_t* src_values, size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr, def_style_res); } - const ResTable& res = theme->getResTable(); + AssetManager2* assetmanager = theme->GetAssetManager(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_bag_type_set_flags = 0; + uint32_t def_style_flags = 0u; if (def_style_attr != 0) { Res_value value; - if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) { + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { if (value.dataType == Res_value::TYPE_REFERENCE) { def_style_res = value.data; } } } - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - // Retrieve the default style bag, if requested. - const ResTable::bag_entry* def_style_start = nullptr; - uint32_t def_style_type_set_flags = 0; - ssize_t bag_off = def_style_res != 0 - ? res.getBagLocked(def_style_res, &def_style_start, - &def_style_type_set_flags) - : -1; - def_style_type_set_flags |= def_style_bag_type_set_flags; - const ResTable::bag_entry* const def_style_end = - def_style_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end); + const ResolvedBag* default_style_bag = nullptr; + if (def_style_res != 0) { + default_style_bag = assetmanager->GetBag(def_style_res); + if (default_style_bag != nullptr) { + def_style_flags |= default_style_bag->type_spec_flags; + } + } + + BagAttributeFinder def_style_attr_finder(default_style_bag); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -106,7 +106,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ssize_t block = -1; + ApkAssetsCookie cookie = kInvalidCookie; uint32_t type_set_flags = 0; value.dataType = Res_value::TYPE_NULL; @@ -122,15 +122,14 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, value.dataType = Res_value::TYPE_ATTRIBUTE; value.data = src_values[ii]; if (kDebugStyles) { - ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, - value.data); + ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data); } } else { - const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident); - if (def_style_entry != def_style_end) { - block = def_style_entry->stringBlock; - type_set_flags = def_style_type_set_flags; - value = def_style_entry->map.value; + const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident); + if (entry != def_style_attr_finder.end()) { + cookie = entry->cookie; + type_set_flags = def_style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -140,22 +139,26 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t resid = 0; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ssize_t new_block = - theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); - if (new_block >= 0) block = new_block; + ApkAssetsCookie new_cookie = + theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } if (kDebugStyles) { ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data); } } else if (value.data != Res_value::DATA_NULL_EMPTY) { - // If we still don't have a value for this attribute, try to find - // it in the theme! - ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); - if (new_block >= 0) { + // If we still don't have a value for this attribute, try to find it in the theme! + ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); + if (new_cookie != kInvalidCookie) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); - if (new_block >= 0) block = new_block; + new_cookie = + assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } if (kDebugStyles) { ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -169,7 +172,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = -1; + cookie = kInvalidCookie; } if (kDebugStyles) { @@ -179,9 +182,7 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != -1 ? static_cast<uint32_t>(res.getTableCookie(block)) - : static_cast<uint32_t>(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -195,90 +196,80 @@ bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, out_values += STYLE_NUM_ENTRIES; } - res.unlock(); - if (out_indices != nullptr) { out_indices[0] = indices_idx; } return true; } -void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { if (kDebugStyles) { - ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", - theme, def_style_attr, def_style_res, xml_parser); + ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, + def_style_attr, def_style_resid, xml_parser); } - const ResTable& res = theme->getResTable(); + AssetManager2* assetmanager = theme->GetAssetManager(); ResTable_config config; Res_value value; int indices_idx = 0; // Load default style from attribute, if specified... - uint32_t def_style_bag_type_set_flags = 0; + uint32_t def_style_flags = 0u; if (def_style_attr != 0) { Res_value value; - if (theme->getAttribute(def_style_attr, &value, - &def_style_bag_type_set_flags) >= 0) { + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { if (value.dataType == Res_value::TYPE_REFERENCE) { - def_style_res = value.data; + def_style_resid = value.data; } } } - // Retrieve the style class associated with the current XML tag. - int style = 0; - uint32_t style_bag_type_set_flags = 0; + // Retrieve the style resource ID associated with the current XML tag's style attribute. + uint32_t style_resid = 0u; + uint32_t style_flags = 0u; if (xml_parser != nullptr) { ssize_t idx = xml_parser->indexOfStyle(); if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) { if (value.dataType == value.TYPE_ATTRIBUTE) { - if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) { + // Resolve the attribute with out theme. + if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) { value.dataType = Res_value::TYPE_NULL; } } + if (value.dataType == value.TYPE_REFERENCE) { - style = value.data; + style_resid = value.data; } } } - // Now lock down the resource object and start pulling stuff from it. - res.lock(); - // Retrieve the default style bag, if requested. - const ResTable::bag_entry* def_style_attr_start = nullptr; - uint32_t def_style_type_set_flags = 0; - ssize_t bag_off = def_style_res != 0 - ? res.getBagLocked(def_style_res, &def_style_attr_start, - &def_style_type_set_flags) - : -1; - def_style_type_set_flags |= def_style_bag_type_set_flags; - const ResTable::bag_entry* const def_style_attr_end = - def_style_attr_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder def_style_attr_finder(def_style_attr_start, - def_style_attr_end); + const ResolvedBag* default_style_bag = nullptr; + if (def_style_resid != 0) { + default_style_bag = assetmanager->GetBag(def_style_resid); + if (default_style_bag != nullptr) { + def_style_flags |= default_style_bag->type_spec_flags; + } + } + + BagAttributeFinder def_style_attr_finder(default_style_bag); // Retrieve the style class bag, if requested. - const ResTable::bag_entry* style_attr_start = nullptr; - uint32_t style_type_set_flags = 0; - bag_off = - style != 0 - ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags) - : -1; - style_type_set_flags |= style_bag_type_set_flags; - const ResTable::bag_entry* const style_attr_end = - style_attr_start + (bag_off >= 0 ? bag_off : 0); - BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end); + const ResolvedBag* xml_style_bag = nullptr; + if (style_resid != 0) { + xml_style_bag = assetmanager->GetBag(style_resid); + if (xml_style_bag != nullptr) { + style_flags |= xml_style_bag->type_spec_flags; + } + } + + BagAttributeFinder xml_style_attr_finder(xml_style_bag); // Retrieve the XML attributes, if requested. - static const ssize_t kXmlBlock = 0x10000000; XmlAttributeFinder xml_attr_finder(xml_parser); - const size_t xml_attr_end = - xml_parser != nullptr ? xml_parser->getAttributeCount() : 0; // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. @@ -289,8 +280,8 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); } - ssize_t block = kXmlBlock; - uint32_t type_set_flags = 0; + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0u; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -302,7 +293,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s // Walk through the xml attributes looking for the requested attribute. const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); - if (xml_attr_idx != xml_attr_end) { + if (xml_attr_idx != xml_attr_finder.end()) { // We found the attribute we were looking for. xml_parser->getAttributeValue(xml_attr_idx, &value); if (kDebugStyles) { @@ -312,12 +303,12 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the style class values looking for the requested attribute. - const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident); - if (style_attr_entry != style_attr_end) { + const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident); + if (entry != xml_style_attr_finder.end()) { // We found the attribute we were looking for. - block = style_attr_entry->stringBlock; - type_set_flags = style_type_set_flags; - value = style_attr_entry->map.value; + cookie = entry->cookie; + type_set_flags = style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data); } @@ -326,25 +317,25 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the default style values looking for the requested attribute. - const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident); - if (def_style_attr_entry != def_style_attr_end) { + const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident); + if (entry != def_style_attr_finder.end()) { // We found the attribute we were looking for. - block = def_style_attr_entry->stringBlock; - type_set_flags = style_type_set_flags; - value = def_style_attr_entry->map.value; + cookie = entry->cookie; + type_set_flags = def_style_flags; + value = entry->value; if (kDebugStyles) { ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); } } } - uint32_t resid = 0; + uint32_t resid = 0u; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - ssize_t new_block = - theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config); - if (new_block >= 0) { - block = new_block; + ApkAssetsCookie new_cookie = + theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; } if (kDebugStyles) { @@ -352,14 +343,15 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s } } else if (value.data != Res_value::DATA_NULL_EMPTY) { // If we still don't have a value for this attribute, try to find it in the theme! - ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags); - if (new_block >= 0) { + ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); + if (new_cookie != kInvalidCookie) { if (kDebugStyles) { ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config); - if (new_block >= 0) { - block = new_block; + new_cookie = + assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; } if (kDebugStyles) { @@ -375,7 +367,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s } value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = kXmlBlock; + cookie = kInvalidCookie; } if (kDebugStyles) { @@ -385,9 +377,7 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != kXmlBlock ? static_cast<uint32_t>(res.getTableCookie(block)) - : static_cast<uint32_t>(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -402,36 +392,28 @@ void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_s out_values += STYLE_NUM_ENTRIES; } - res.unlock(); - // out_indices must NOT be nullptr. out_indices[0] = indices_idx; } -bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, - uint32_t* attrs, size_t attrs_length, - uint32_t* out_values, uint32_t* out_indices) { +bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { ResTable_config config; Res_value value; int indices_idx = 0; - // Now lock down the resource object and start pulling stuff from it. - res->lock(); - // Retrieve the XML attributes, if requested. const size_t xml_attr_count = xml_parser->getAttributeCount(); size_t ix = 0; uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix); - static const ssize_t kXmlBlock = 0x10000000; - // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - ssize_t block = kXmlBlock; - uint32_t type_set_flags = 0; + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0u; value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; @@ -450,28 +432,27 @@ bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, cur_xml_attr = xml_parser->getAttributeNameResID(ix); } - uint32_t resid = 0; + uint32_t resid = 0u; if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - // printf("Resolving attribute reference\n"); - ssize_t new_block = res->resolveReference(&value, block, &resid, - &type_set_flags, &config); - if (new_block >= 0) block = new_block; + ApkAssetsCookie new_cookie = + assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } } // Deal with the special @null value -- it turns back to TYPE_NULL. if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - block = kXmlBlock; + cookie = kInvalidCookie; } // Write the final value back to Java. out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = - block != kXmlBlock ? static_cast<uint32_t>(res->getTableCookie(block)) - : static_cast<uint32_t>(-1); + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); out_values[STYLE_RESOURCE_ID] = resid; out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; out_values[STYLE_DENSITY] = config.density; @@ -485,8 +466,6 @@ bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, out_values += STYLE_NUM_ENTRIES; } - res->unlock(); - if (out_indices != nullptr) { out_indices[0] = indices_idx; } diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 28548e27baf0..04d506a2d71c 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -44,44 +44,6 @@ namespace android { constexpr const static int kAppPackageId = 0x7f; -// Element of a TypeSpec array. See TypeSpec. -struct Type { - // The configuration for which this type defines entries. - // This is already converted to host endianness. - ResTable_config configuration; - - // Pointer to the mmapped data where entry definitions are kept. - const ResTable_type* type; -}; - -// TypeSpec is going to be immediately proceeded by -// an array of Type structs, all in the same block of memory. -struct TypeSpec { - // Pointer to the mmapped data where flags are kept. - // Flags denote whether the resource entry is public - // and under which configurations it varies. - const ResTable_typeSpec* type_spec; - - // Pointer to the mmapped data where the IDMAP mappings for this type - // exist. May be nullptr if no IDMAP exists. - const IdmapEntry_header* idmap_entries; - - // The number of types that follow this struct. - // There is a type for each configuration - // that entries are defined for. - size_t type_count; - - // Trick to easily access a variable number of Type structs - // proceeding this struct, and to ensure their alignment. - const Type types[0]; -}; - -// TypeSpecPtr points to the block of memory that holds -// a TypeSpec struct, followed by an array of Type structs. -// TypeSpecPtr is a managed pointer that knows how to delete -// itself. -using TypeSpecPtr = util::unique_cptr<TypeSpec>; - namespace { // Builder that helps accumulate Type structs and then create a single @@ -95,21 +57,22 @@ class TypeSpecPtrBuilder { } void AddType(const ResTable_type* type) { - ResTable_config config; - config.copyFromDtoH(type->config); - types_.push_back(Type{config, type}); + types_.push_back(type); } TypeSpecPtr Build() { // Check for overflow. - if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) { + using ElementType = const ResTable_type*; + if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(ElementType) < + types_.size()) { return {}; } - TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type))); + TypeSpec* type_spec = + (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType))); type_spec->type_spec = header_; type_spec->idmap_entries = idmap_header_; type_spec->type_count = types_.size(); - memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type)); + memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType)); return TypeSpecPtr(type_spec); } @@ -118,7 +81,7 @@ class TypeSpecPtrBuilder { const ResTable_typeSpec* header_; const IdmapEntry_header* idmap_header_; - std::vector<Type> types_; + std::vector<const ResTable_type*> types_; }; } // namespace @@ -162,18 +125,17 @@ static bool VerifyResTableType(const ResTable_type* header) { return true; } -static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset, - size_t entry_idx) { +static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) { // Check that the offset is aligned. if (entry_offset & 0x03) { - LOG(ERROR) << "Entry offset at index " << entry_idx << " is not 4-byte aligned."; + LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned."; return false; } // Check that the offset doesn't overflow. if (entry_offset > std::numeric_limits<uint32_t>::max() - dtohl(type->entriesStart)) { // Overflow in offset. - LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large."; + LOG(ERROR) << "Entry at offset " << entry_offset << " is too large."; return false; } @@ -181,7 +143,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset entry_offset += dtohl(type->entriesStart); if (entry_offset > chunk_size - sizeof(ResTable_entry)) { - LOG(ERROR) << "Entry offset at index " << entry_idx + LOG(ERROR) << "Entry at offset " << entry_offset << " is too large. No room for ResTable_entry."; return false; } @@ -191,13 +153,13 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t entry_size = dtohs(entry->size); if (entry_size < sizeof(*entry)) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx + LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too small."; return false; } if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) { - LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx + LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too large."; return false; } @@ -205,7 +167,7 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset if (entry_size < sizeof(ResTable_map_entry)) { // There needs to be room for one Res_value struct. if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) { - LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << entry_idx + LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset << " for type " << (int)type->id << "."; return false; } @@ -214,12 +176,12 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset reinterpret_cast<const Res_value*>(reinterpret_cast<const uint8_t*>(entry) + entry_size); const size_t value_size = dtohs(value->size); if (value_size < sizeof(Res_value)) { - LOG(ERROR) << "Res_value at index " << entry_idx << " is too small."; + LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small."; return false; } if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) { - LOG(ERROR) << "Res_value size " << value_size << " at index " << entry_idx + LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset << " is too large."; return false; } @@ -228,119 +190,76 @@ static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset const size_t map_entry_count = dtohl(map->count); size_t map_entries_start = entry_offset + entry_size; if (map_entries_start & 0x03) { - LOG(ERROR) << "Map entries at index " << entry_idx << " start at unaligned offset."; + LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset."; return false; } // Each entry is sizeof(ResTable_map) big. if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) { - LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << entry_idx << "."; + LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << "."; return false; } } return true; } -bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_idx, - const ResTable_config& config, FindEntryResult* out_entry) const { - const ResTable_config* best_config = nullptr; - const ResTable_type* best_type = nullptr; - uint32_t best_offset = 0; - - for (uint32_t i = 0; i < type_spec_ptr->type_count; i++) { - const Type* type = &type_spec_ptr->types[i]; - const ResTable_type* type_chunk = type->type; - - if (type->configuration.match(config) && - (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const size_t entry_count = dtohl(type_chunk->entryCount); - const size_t offsets_offset = dtohs(type_chunk->header.headerSize); - - // Check if there is the desired entry in this type. - - if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { - // This is encoded as a sparse map, so perform a binary search. - const ResTable_sparseTypeEntry* sparse_indices = - reinterpret_cast<const ResTable_sparseTypeEntry*>( - reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset); - const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; - const ResTable_sparseTypeEntry* result = - std::lower_bound(sparse_indices, sparse_indices_end, entry_idx, - [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { - return dtohs(entry.idx) < entry_idx; - }); - - if (result == sparse_indices_end || dtohs(result->idx) != entry_idx) { - // No entry found. - continue; - } - - // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as - // the real offset divided by 4. - best_offset = uint32_t{dtohs(result->offset)} * 4u; - } else { - if (entry_idx >= entry_count) { - // This entry cannot be here. - continue; - } +const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk, + uint16_t entry_index) { + uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index); + if (entry_offset == ResTable_type::NO_ENTRY) { + return nullptr; + } + return GetEntryFromOffset(type_chunk, entry_offset); +} - const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>( - reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset); - const uint32_t offset = dtohl(entry_offsets[entry_idx]); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } +uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) { + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const size_t entry_count = dtohl(type_chunk->entryCount); + const size_t offsets_offset = dtohs(type_chunk->header.headerSize); - // There is an entry for this resource, record it. - best_offset = offset; - } + // Check if there is the desired entry in this type. - best_config = &type->configuration; - best_type = type_chunk; + if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { + // This is encoded as a sparse map, so perform a binary search. + const ResTable_sparseTypeEntry* sparse_indices = + reinterpret_cast<const ResTable_sparseTypeEntry*>( + reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset); + const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; + const ResTable_sparseTypeEntry* result = + std::lower_bound(sparse_indices, sparse_indices_end, entry_index, + [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { + return dtohs(entry.idx) < entry_idx; + }); + + if (result == sparse_indices_end || dtohs(result->idx) != entry_index) { + // No entry found. + return ResTable_type::NO_ENTRY; } - } - if (best_type == nullptr) { - return false; + // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as + // the real offset divided by 4. + return uint32_t{dtohs(result->offset)} * 4u; } - if (UNLIKELY(!VerifyResTableEntry(best_type, best_offset, entry_idx))) { - return false; + // This type is encoded as a dense array. + if (entry_index >= entry_count) { + // This entry cannot be here. + return ResTable_type::NO_ENTRY; } - const ResTable_entry* best_entry = reinterpret_cast<const ResTable_entry*>( - reinterpret_cast<const uint8_t*>(best_type) + best_offset + dtohl(best_type->entriesStart)); - - const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec_ptr->type_spec + 1); - out_entry->type_flags = dtohl(flags[entry_idx]); - out_entry->entry = best_entry; - out_entry->config = best_config; - out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1); - out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index)); - return true; + const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>( + reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset); + return dtohl(entry_offsets[entry_index]); } -bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, - FindEntryResult* out_entry) const { - ATRACE_CALL(); - - // If the type IDs are offset in this package, we need to take that into account when searching - // for a type. - const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_]; - if (UNLIKELY(ptr == nullptr)) { - return false; +const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk, + uint32_t offset) { + if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) { + return nullptr; } - - // If there is an IDMAP supplied with this package, translate the entry ID. - if (ptr->idmap_entries != nullptr) { - if (!LoadedIdmap::Lookup(ptr->idmap_entries, entry_idx, &entry_idx)) { - // There is no mapping, so the resource is not meant to be in this overlay package. - return false; - } - } - return FindEntry(ptr, entry_idx, config, out_entry); + return reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type_chunk) + + offset + dtohl(type_chunk->entriesStart)); } void LoadedPackage::CollectConfigurations(bool exclude_mipmap, @@ -348,7 +267,7 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, const static std::u16string kMipMap = u"mipmap"; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const util::unique_cptr<TypeSpec>& type_spec = type_specs_[i]; + const TypeSpecPtr& type_spec = type_specs_[i]; if (type_spec != nullptr) { if (exclude_mipmap) { const int type_idx = type_spec->type_spec->id - 1; @@ -369,8 +288,11 @@ void LoadedPackage::CollectConfigurations(bool exclude_mipmap, } } - for (size_t j = 0; j < type_spec->type_count; j++) { - out_configs->insert(type_spec->types[j].configuration); + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config config; + config.copyFromDtoH((*iter)->config); + out_configs->insert(config); } } } @@ -380,10 +302,12 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set<std::string>* out char temp_locale[RESTABLE_MAX_LOCALE_LEN]; const size_t type_count = type_specs_.size(); for (size_t i = 0; i < type_count; i++) { - const util::unique_cptr<TypeSpec>& type_spec = type_specs_[i]; + const TypeSpecPtr& type_spec = type_specs_[i]; if (type_spec != nullptr) { - for (size_t j = 0; j < type_spec->type_count; j++) { - const ResTable_config& configuration = type_spec->types[j].configuration; + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config configuration; + configuration.copyFromDtoH((*iter)->config); if (configuration.locale != 0) { configuration.getBcp47Locale(temp_locale, canonicalize); std::string locale(temp_locale); @@ -411,17 +335,17 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } - for (size_t ti = 0; ti < type_spec->type_count; ti++) { - const Type* type = &type_spec->types[ti]; - size_t entry_count = dtohl(type->type->entryCount); + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + const ResTable_type* type = *iter; + size_t entry_count = dtohl(type->entryCount); for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) { const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>( - reinterpret_cast<const uint8_t*>(type->type) + dtohs(type->type->header.headerSize)); + reinterpret_cast<const uint8_t*>(type) + dtohs(type->header.headerSize)); const uint32_t offset = dtohl(entry_offsets[entry_idx]); if (offset != ResTable_type::NO_ENTRY) { - const ResTable_entry* entry = - reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type->type) + - dtohl(type->type->entriesStart) + offset); + const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>( + reinterpret_cast<const uint8_t*>(type) + dtohl(type->entriesStart) + offset); if (dtohl(entry->key.index) == static_cast<uint32_t>(key_idx)) { // The package ID will be overridden by the caller (due to runtime assignment of package // IDs for shared libraries). @@ -433,8 +357,7 @@ uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, return 0u; } -const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const { - const uint8_t package_id = get_package_id(resid); +const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { for (const auto& loaded_package : packages_) { if (loaded_package->GetPackageId() == package_id) { return loaded_package.get(); @@ -446,7 +369,7 @@ const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const { std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool system, bool load_as_shared_library) { - ATRACE_CALL(); + ATRACE_NAME("LoadedPackage::Load"); std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage()); // typeIdOffset was added at some point, but we still must recognize apps built before this @@ -486,14 +409,10 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, util::ReadUtf16StringFromDevice(header->name, arraysize(header->name), &loaded_package->package_name_); - // A TypeSpec builder. We use this to accumulate the set of Types - // available for a TypeSpec, and later build a single, contiguous block - // of memory that holds all the Types together with the TypeSpec. - std::unique_ptr<TypeSpecPtrBuilder> types_builder; - - // Keep track of the last seen type index. Since type IDs are 1-based, - // this records their index, which is 0-based (type ID - 1). - uint8_t last_type_idx = 0; + // A map of TypeSpec builders, each associated with an type index. + // We use these to accumulate the set of Types available for a TypeSpec, and later build a single, + // contiguous block of memory that holds all the Types together with the TypeSpec. + std::unordered_map<int, std::unique_ptr<TypeSpecPtrBuilder>> type_builder_map; ChunkIterator iter(chunk.data_ptr(), chunk.data_size()); while (iter.HasNext()) { @@ -525,30 +444,6 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_TYPE_SPEC_TYPE: { - ATRACE_NAME("LoadTableTypeSpec"); - - // Starting a new TypeSpec, so finish the old one if there was one. - if (types_builder) { - TypeSpecPtr type_spec_ptr = types_builder->Build(); - if (type_spec_ptr == nullptr) { - LOG(ERROR) << "Too many type configurations, overflow detected."; - return {}; - } - - // We only add the type to the package if there is no IDMAP, or if the type is - // overlaying something. - if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) { - // If this is an overlay, insert it at the target type ID. - if (type_spec_ptr->idmap_entries != nullptr) { - last_type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; - } - loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); - } - - types_builder = {}; - last_type_idx = 0; - } - const ResTable_typeSpec* type_spec = child_chunk.header<ResTable_typeSpec>(); if (type_spec == nullptr) { LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small."; @@ -583,8 +478,6 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, return {}; } - last_type_idx = type_spec->id - 1; - // If this is an overlay, associate the mapping of this type to the target type // from the IDMAP. const IdmapEntry_header* idmap_entry_header = nullptr; @@ -592,7 +485,13 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, idmap_entry_header = loaded_idmap->GetEntryMapForType(type_spec->id); } - types_builder = util::make_unique<TypeSpecPtrBuilder>(type_spec, idmap_entry_header); + std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type_spec->id - 1]; + if (builder_ptr == nullptr) { + builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec, idmap_entry_header); + } else { + LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x", + type_spec->id); + } } break; case RES_TABLE_TYPE_TYPE: { @@ -607,12 +506,15 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, } // Type chunks must be preceded by their TypeSpec chunks. - if (!types_builder || type->id - 1 != last_type_idx) { - LOG(ERROR) << "RES_TABLE_TYPE_TYPE found without preceding RES_TABLE_TYPE_SPEC_TYPE."; + std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type->id - 1]; + if (builder_ptr != nullptr) { + builder_ptr->AddType(type); + } else { + LOG(ERROR) << StringPrintf( + "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.", + type->id); return {}; } - - types_builder->AddType(type); } break; case RES_TABLE_LIBRARY_TYPE: { @@ -638,7 +540,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, arraysize(entry_iter->packageName), &package_name); if (dtohl(entry_iter->packageId) >= std::numeric_limits<uint8_t>::max()) { - LOG(ERROR) << base::StringPrintf( + LOG(ERROR) << StringPrintf( "Package ID %02x in RES_TABLE_LIBRARY_TYPE too large for package '%s'.", dtohl(entry_iter->packageId), package_name.c_str()); return {}; @@ -651,14 +553,20 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, } break; default: - LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type()); break; } } - // Finish the last TypeSpec. - if (types_builder) { - TypeSpecPtr type_spec_ptr = types_builder->Build(); + if (iter.HadError()) { + LOG(ERROR) << iter.GetLastError(); + return {}; + } + + // Flatten and construct the TypeSpecs. + for (auto& entry : type_builder_map) { + uint8_t type_idx = static_cast<uint8_t>(entry.first); + TypeSpecPtr type_spec_ptr = entry.second->Build(); if (type_spec_ptr == nullptr) { LOG(ERROR) << "Too many type configurations, overflow detected."; return {}; @@ -669,43 +577,17 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, if (loaded_idmap == nullptr || type_spec_ptr->idmap_entries != nullptr) { // If this is an overlay, insert it at the target type ID. if (type_spec_ptr->idmap_entries != nullptr) { - last_type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; + type_idx = dtohs(type_spec_ptr->idmap_entries->target_type_id) - 1; } - loaded_package->type_specs_.editItemAt(last_type_idx) = std::move(type_spec_ptr); + loaded_package->type_specs_.editItemAt(type_idx) = std::move(type_spec_ptr); } } - if (iter.HadError()) { - LOG(ERROR) << iter.GetLastError(); - return {}; - } return std::move(loaded_package); } -bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config, - FindEntryResult* out_entry) const { - ATRACE_CALL(); - - const uint8_t package_id = get_package_id(resid); - const uint8_t type_id = get_type_id(resid); - const uint16_t entry_id = get_entry_id(resid); - - if (UNLIKELY(type_id == 0)) { - LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); - return false; - } - - for (const auto& loaded_package : packages_) { - if (loaded_package->GetPackageId() == package_id) { - return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry); - } - } - return false; -} - bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, bool load_as_shared_library) { - ATRACE_CALL(); const ResTable_header* header = chunk.header<ResTable_header>(); if (header == nullptr) { LOG(ERROR) << "RES_TABLE_TYPE too small."; @@ -752,7 +634,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, } break; default: - LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type()); break; } } @@ -767,7 +649,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data, const LoadedIdmap* loaded_idmap, bool system, bool load_as_shared_library) { - ATRACE_CALL(); + ATRACE_NAME("LoadedArsc::LoadTable"); // Not using make_unique because the constructor is private. std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc()); @@ -784,7 +666,7 @@ std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data, break; default: - LOG(WARNING) << base::StringPrintf("Unknown chunk type '%02x'.", chunk.type()); + LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type()); break; } } diff --git a/libs/androidfw/OWNERS b/libs/androidfw/OWNERS new file mode 100644 index 000000000000..23ec5ab0d1f3 --- /dev/null +++ b/libs/androidfw/OWNERS @@ -0,0 +1,2 @@ +set noparent +toddke@google.com diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index b033137b4764..ef08897d997a 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -69,6 +69,8 @@ struct ResolvedBag { Entry entries[0]; }; +struct FindEntryResult; + // AssetManager2 is the main entry point for accessing assets and resources. // AssetManager2 provides caching of resources retrieved via the underlying ApkAssets. class AssetManager2 { @@ -127,7 +129,7 @@ class AssetManager2 { // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap' // will be excluded from the list. std::set<ResTable_config> GetResourceConfigurations(bool exclude_system = false, - bool exclude_mipmap = false); + bool exclude_mipmap = false) const; // Returns all the locales for which there are resources defined. This includes resource // locales in all the ApkAssets set for this AssetManager. @@ -136,24 +138,24 @@ class AssetManager2 { // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized // and de-duped in the resulting list. std::set<std::string> GetResourceLocales(bool exclude_system = false, - bool merge_equivalent_languages = false); + bool merge_equivalent_languages = false) const; // Searches the set of APKs loaded by this AssetManager and opens the first one found located // in the assets/ directory. // `mode` controls how the file is opened. // // NOTE: The loaded APKs are searched in reverse order. - std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode); + std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode) const; // Opens a file within the assets/ directory of the APK specified by `cookie`. // `mode` controls how the file is opened. std::unique_ptr<Asset> Open(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode); + Asset::AccessMode mode) const; // Opens the directory specified by `dirname`. The result is an AssetDir that is the combination // of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded. // The entries are sorted by their ASCII name. - std::unique_ptr<AssetDir> OpenDir(const std::string& dirname); + std::unique_ptr<AssetDir> OpenDir(const std::string& dirname) const; // Searches the set of APKs loaded by this AssetManager and opens the first one found. // `mode` controls how the file is opened. @@ -161,24 +163,24 @@ class AssetManager2 { // // NOTE: The loaded APKs are searched in reverse order. std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, Asset::AccessMode mode, - ApkAssetsCookie* out_cookie = nullptr); + ApkAssetsCookie* out_cookie = nullptr) const; // Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened. // This is typically used to open a specific AndroidManifest.xml, or a binary XML file // referenced by a resource lookup with GetResource(). std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie, - Asset::AccessMode mode); + Asset::AccessMode mode) const; // Populates the `out_name` parameter with resource name information. // Utf8 strings are preferred, and only if they are unavailable are // the Utf16 variants populated. // Returns false if the resource was not found or the name was missing/corrupt. - bool GetResourceName(uint32_t resid, ResourceName* out_name); + bool GetResourceName(uint32_t resid, ResourceName* out_name) const; // Populates `out_flags` with the bitmask of configuration axis that this resource varies with. // See ResTable_config for the list of configuration axis. // Returns false if the resource was not found. - bool GetResourceFlags(uint32_t resid, uint32_t* out_flags); + bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const; // Finds the resource ID assigned to `resource_name`. // `resource_name` must be of the form '[package:][type/]entry'. @@ -186,7 +188,7 @@ class AssetManager2 { // If no type is specified in `resource_name`, then `fallback_type` is used as the type. // Returns 0x0 if no resource by that name was found. uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {}, - const std::string& fallback_package = {}); + const std::string& fallback_package = {}) const; // Retrieves the best matching resource with ID `resid`. The resource value is filled into // `out_value` and the configuration for the selected value is populated in `out_selected_config`. @@ -199,7 +201,7 @@ class AssetManager2 { // this function logs if the resource was a map/bag type before returning kInvalidCookie. ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, Res_value* out_value, ResTable_config* out_selected_config, - uint32_t* out_flags); + uint32_t* out_flags) const; // Resolves the resource reference in `in_out_value` if the data type is // Res_value::TYPE_REFERENCE. @@ -215,7 +217,7 @@ class AssetManager2 { // it was not found. ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config, uint32_t* in_out_flags, - uint32_t* out_last_reference); + uint32_t* out_last_reference) const; // Retrieves the best matching bag/map resource with ID `resid`. // This method will resolve all parent references for this bag and merge keys with the child. @@ -233,9 +235,9 @@ class AssetManager2 { std::unique_ptr<Theme> NewTheme(); template <typename Func> - void ForEachPackage(Func func) { + void ForEachPackage(Func func) const { for (const PackageGroup& package_group : package_groups_) { - func(package_group.packages_.front()->GetPackageName(), + func(package_group.packages_.front().loaded_package_->GetPackageName(), package_group.dynamic_ref_table.mAssignedPackageId); } } @@ -260,7 +262,7 @@ class AssetManager2 { // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds. ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, - FindEntryResult* out_entry); + FindEntryResult* out_entry) const; // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. @@ -270,13 +272,43 @@ class AssetManager2 { // bitmask `diff`. void InvalidateCaches(uint32_t diff); + // Triggers the re-construction of lists of types that match the set configuration. + // This should always be called when mutating the AssetManager's configuration or ApkAssets set. + void RebuildFilterList(); + // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. std::vector<const ApkAssets*> apk_assets_; + // A collection of configurations and their associated ResTable_type that match the current + // AssetManager configuration. + struct FilteredConfigGroup { + std::vector<ResTable_config> configurations; + std::vector<const ResTable_type*> types; + }; + + // Represents an single package. + struct ConfiguredPackage { + // A pointer to the immutable, loaded package info. + const LoadedPackage* loaded_package_; + + // A mutable AssetManager-specific list of configurations that match the AssetManager's + // current configuration. This is used as an optimization to avoid checking every single + // candidate configuration when looking up resources. + ByteBucketArray<FilteredConfigGroup> filtered_configs_; + }; + + // Represents a logical package, which can be made up of many individual packages. Each package + // in a PackageGroup shares the same package name and package ID. struct PackageGroup { - std::vector<const LoadedPackage*> packages_; + // The set of packages that make-up this group. + std::vector<ConfiguredPackage> packages_; + + // The cookies associated with each package in the group. They share the same order as + // packages_. std::vector<ApkAssetsCookie> cookies_; + + // A library reference table that contains build-package ID to runtime-package ID mappings. DynamicRefTable dynamic_ref_table; }; @@ -350,7 +382,7 @@ class Theme { ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, ResTable_config* in_out_selected_config = nullptr, uint32_t* in_out_type_spec_flags = nullptr, - uint32_t* out_last_ref = nullptr); + uint32_t* out_last_ref = nullptr) const; private: DISALLOW_COPY_AND_ASSIGN(Theme); diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h index f281921824e7..03fad4947dfe 100644 --- a/libs/androidfw/include/androidfw/AttributeFinder.h +++ b/libs/androidfw/include/androidfw/AttributeFinder.h @@ -58,6 +58,7 @@ class BackTrackingAttributeFinder { BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end); Iterator Find(uint32_t attr); + inline Iterator end(); private: void JumpToClosestAttribute(uint32_t package_id); @@ -201,6 +202,11 @@ Iterator BackTrackingAttributeFinder<Derived, Iterator>::Find(uint32_t attr) { return end_; } +template <typename Derived, typename Iterator> +Iterator BackTrackingAttributeFinder<Derived, Iterator>::end() { + return end_; +} + } // namespace android #endif // ANDROIDFW_ATTRIBUTE_FINDER_H diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h index 69b760414846..35ef98d8c704 100644 --- a/libs/androidfw/include/androidfw/AttributeResolution.h +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -17,7 +17,8 @@ #ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H #define ANDROIDFW_ATTRIBUTERESOLUTION_H -#include <androidfw/ResourceTypes.h> +#include "androidfw/AssetManager2.h" +#include "androidfw/ResourceTypes.h" namespace android { @@ -42,19 +43,19 @@ enum { // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, +bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid, uint32_t* src_values, size_t src_values_length, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` is NOT optional and must NOT be nullptr. -void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, - uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, +void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs, +bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); } // namespace android diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 965e2dbd2fb2..35ae5fcd9e7b 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -41,32 +41,40 @@ class DynamicPackageEntry { int package_id = 0; }; -struct FindEntryResult { - // A pointer to the resource table entry for this resource. - // If the size of the entry is > sizeof(ResTable_entry), it can be cast to - // a ResTable_map_entry and processed as a bag/map. - const ResTable_entry* entry = nullptr; - - // The configuration for which the resulting entry was defined. - const ResTable_config* config = nullptr; - - // Stores the resulting bitmask of configuration axis with which the resource value varies. - uint32_t type_flags = 0u; - - // The dynamic package ID map for the package from which this resource came from. - const DynamicRefTable* dynamic_ref_table = nullptr; - - // The string pool reference to the type's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef type_string_ref; - - // The string pool reference to the entry's name. This uses a different string pool than - // the global string pool, but this is hidden from the caller. - StringPoolRef entry_string_ref; +// TypeSpec is going to be immediately proceeded by +// an array of Type structs, all in the same block of memory. +struct TypeSpec { + // Pointer to the mmapped data where flags are kept. + // Flags denote whether the resource entry is public + // and under which configurations it varies. + const ResTable_typeSpec* type_spec; + + // Pointer to the mmapped data where the IDMAP mappings for this type + // exist. May be nullptr if no IDMAP exists. + const IdmapEntry_header* idmap_entries; + + // The number of types that follow this struct. + // There is a type for each configuration that entries are defined for. + size_t type_count; + + // Trick to easily access a variable number of Type structs + // proceeding this struct, and to ensure their alignment. + const ResTable_type* types[0]; + + inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const { + if (entry_index >= dtohl(type_spec->entryCount)) { + return 0u; + } + + const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec + 1); + return flags[entry_index]; + } }; -struct TypeSpec; -class LoadedArsc; +// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of +// ResTable_type pointers. +// TypeSpecPtr is a managed pointer that knows how to delete itself. +using TypeSpecPtr = util::unique_cptr<TypeSpec>; class LoadedPackage { public: @@ -76,9 +84,6 @@ class LoadedPackage { ~LoadedPackage(); - bool FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config, - FindEntryResult* out_entry) const; - // Finds the entry with the specified type name and entry name. The names are in UTF-16 because // the underlying ResStringPool API expects this. For now this is acceptable, but since // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. @@ -86,6 +91,12 @@ class LoadedPackage { // for patching the correct package ID to the resource ID. uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; + static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index); + + static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index); + + static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset); + // Returns the string pool where type names are stored. inline const ResStringPool* GetTypeStringPool() const { return &type_string_pool_; @@ -135,14 +146,32 @@ class LoadedPackage { // before being inserted into the set. This may cause some equivalent locales to de-dupe. void CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const; + // type_idx is TT - 1 from 0xPPTTEEEE. + inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const { + // If the type IDs are offset in this package, we need to take that into account when searching + // for a type. + return type_specs_[type_index - type_id_offset_].get(); + } + + template <typename Func> + void ForEachTypeSpec(Func f) const { + for (size_t i = 0; i < type_specs_.size(); i++) { + const TypeSpecPtr& ptr = type_specs_[i]; + if (ptr != nullptr) { + uint8_t type_id = ptr->type_spec->id; + if (ptr->idmap_entries != nullptr) { + type_id = ptr->idmap_entries->target_type_id; + } + f(ptr.get(), type_id - 1); + } + } + } + private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); LoadedPackage(); - bool FindEntry(const util::unique_cptr<TypeSpec>& type_spec_ptr, uint16_t entry_idx, - const ResTable_config& config, FindEntryResult* out_entry) const; - ResStringPool type_string_pool_; ResStringPool key_string_pool_; std::string package_name_; @@ -152,7 +181,7 @@ class LoadedPackage { bool system_ = false; bool overlay_ = false; - ByteBucketArray<util::unique_cptr<TypeSpec>> type_specs_; + ByteBucketArray<TypeSpecPtr> type_specs_; std::vector<DynamicPackageEntry> dynamic_package_map_; }; @@ -180,25 +209,20 @@ class LoadedArsc { return &global_string_pool_; } - // Finds the resource with ID `resid` with the best value for configuration `config`. - // The parameter `out_entry` will be filled with the resulting resource entry. - // The resource entry can be a simple entry (ResTable_entry) or a complex bag - // (ResTable_entry_map). - bool FindEntry(uint32_t resid, const ResTable_config& config, FindEntryResult* out_entry) const; + // Gets a pointer to the package with the specified package ID, or nullptr if no such package + // exists. + const LoadedPackage* GetPackageById(uint8_t package_id) const; - // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist. - const LoadedPackage* GetPackageForId(uint32_t resid) const; + // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. + inline const std::vector<std::unique_ptr<const LoadedPackage>>& GetPackages() const { + return packages_; + } // Returns true if this is a system provided resource. inline bool IsSystem() const { return system_; } - // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc. - inline const std::vector<std::unique_ptr<const LoadedPackage>>& GetPackages() const { - return packages_; - } - private: DISALLOW_COPY_AND_ASSIGN(LoadedArsc); diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h new file mode 100644 index 000000000000..64924f433245 --- /dev/null +++ b/libs/androidfw/include/androidfw/MutexGuard.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROIDFW_MUTEXGUARD_H +#define ANDROIDFW_MUTEXGUARD_H + +#include <mutex> +#include <type_traits> + +#include "android-base/macros.h" + +namespace android { + +template <typename T> +class ScopedLock; + +// Owns the guarded object and protects access to it via a mutex. +// The guarded object is inaccessible via this class. +// The mutex is locked and the object accessed via the ScopedLock<T> class. +// +// NOTE: The template parameter T should not be a raw pointer, since ownership +// is ambiguous and error-prone. Instead use an std::unique_ptr<>. +// +// Example use: +// +// Guarded<std::string> shared_string("hello"); +// { +// ScopedLock<std::string> locked_string(shared_string); +// *locked_string += " world"; +// } +// +template <typename T> +class Guarded { + static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer"); + + public: + explicit Guarded() : guarded_() { + } + + template <typename U = T> + explicit Guarded(const T& guarded, + typename std::enable_if<std::is_copy_constructible<U>::value>::type = void()) + : guarded_(guarded) { + } + + template <typename U = T> + explicit Guarded(T&& guarded, + typename std::enable_if<std::is_move_constructible<U>::value>::type = void()) + : guarded_(std::move(guarded)) { + } + + private: + friend class ScopedLock<T>; + + DISALLOW_COPY_AND_ASSIGN(Guarded); + + std::mutex lock_; + T guarded_; +}; + +template <typename T> +class ScopedLock { + public: + explicit ScopedLock(Guarded<T>& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) { + } + + T& operator*() { + return guarded_; + } + + T* operator->() { + return &guarded_; + } + + T* get() { + return &guarded_; + } + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedLock); + + std::lock_guard<std::mutex> lock_; + T& guarded_; +}; + +} // namespace android + +#endif // ANDROIDFW_MUTEXGUARD_H diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h index c2eae855bb7b..d94779bf5225 100644 --- a/libs/androidfw/include/androidfw/ResourceUtils.h +++ b/libs/androidfw/include/androidfw/ResourceUtils.h @@ -28,7 +28,7 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin StringPiece* out_entry); inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) { - return resid | (static_cast<uint32_t>(package_id) << 24); + return (resid & 0x00ffffffu) | (static_cast<uint32_t>(package_id) << 24); } inline uint8_t get_package_id(uint32_t resid) { diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index 6c43a67e602f..e2b9f0040989 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -26,58 +26,56 @@ using ::android::base::unique_fd; using ::com::android::basic::R; +using ::testing::Eq; +using ::testing::Ge; +using ::testing::NotNull; +using ::testing::SizeIs; +using ::testing::StrEq; namespace android { TEST(ApkAssetsTest, LoadApk) { std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - - const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); - ASSERT_NE(nullptr, loaded_package); - - std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml"); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); + ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkFromFd) { const std::string path = GetTestDataPath() + "/basic/basic.apk"; unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY)); - ASSERT_GE(fd.get(), 0); + ASSERT_THAT(fd.get(), Ge(0)); std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - - const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); - ASSERT_NE(nullptr, loaded_package); - - std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml"); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull()); + ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull()); } TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); + const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic()); loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); loaded_arsc = loaded_apk->GetLoadedArsc(); - ASSERT_NE(nullptr, loaded_arsc); - ASSERT_EQ(1u, loaded_arsc->GetPackages().size()); + ASSERT_THAT(loaded_arsc, NotNull()); + ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic()); } @@ -86,19 +84,22 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { ResTable target_table; const std::string target_path = GetTestDataPath() + "/basic/basic.apk"; ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, target_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); + ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/), + Eq(NO_ERROR)); ResTable overlay_table; const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk"; ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/)); + ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/), + Eq(NO_ERROR)); util::unique_cptr<void> idmap_data; void* temp_data; size_t idmap_len; - ASSERT_EQ(NO_ERROR, target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), - overlay_path.c_str(), &temp_data, &idmap_len)); + ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(), + overlay_path.c_str(), &temp_data, &idmap_len), + Eq(NO_ERROR)); idmap_data.reset(temp_data); TemporaryFile tf; @@ -108,37 +109,30 @@ TEST(ApkAssetsTest, LoadApkWithIdmap) { // Open something so that the destructor of TemporaryFile closes a valid fd. tf.fd = open("/dev/null", O_WRONLY); - std::unique_ptr<const ApkAssets> loaded_overlay_apk = ApkAssets::LoadOverlay(tf.path); - ASSERT_NE(nullptr, loaded_overlay_apk); + ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull()); } TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) { std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); - { - std::unique_ptr<Asset> assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, assets); - } + { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } - { - std::unique_ptr<Asset> assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER); - ASSERT_NE(nullptr, assets); - } + { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); } } TEST(ApkAssetsTest, OpenUncompressedAssetFd) { std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); - ASSERT_NE(nullptr, loaded_apk); + ASSERT_THAT(loaded_apk, NotNull()); auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN); - ASSERT_NE(nullptr, asset); + ASSERT_THAT(asset, NotNull()); off64_t start, length; unique_fd fd(asset->openFileDescriptor(&start, &length)); - EXPECT_GE(fd.get(), 0); + ASSERT_THAT(fd.get(), Ge(0)); lseek64(fd.get(), start, SEEK_SET); @@ -146,7 +140,7 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) { buffer.resize(length); ASSERT_TRUE(base::ReadFully(fd.get(), &*buffer.begin(), length)); - EXPECT_EQ("This should be uncompressed.\n\n", buffer); + EXPECT_THAT(buffer, StrEq("This should be uncompressed.\n\n")); } } // namespace android diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp index 85e8f25394e9..437e14772964 100644 --- a/libs/androidfw/tests/AssetManager2_bench.cpp +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -81,17 +81,18 @@ static void BM_AssetManagerLoadFrameworkAssetsOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerLoadFrameworkAssetsOld); -static void BM_AssetManagerGetResource(benchmark::State& state) { - GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, - basic::R::integer::number1, state); +static void BM_AssetManagerGetResource(benchmark::State& state, uint32_t resid) { + GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid, state); } -BENCHMARK(BM_AssetManagerGetResource); +BENCHMARK_CAPTURE(BM_AssetManagerGetResource, number1, basic::R::integer::number1); +BENCHMARK_CAPTURE(BM_AssetManagerGetResource, deep_ref, basic::R::integer::deep_ref); -static void BM_AssetManagerGetResourceOld(benchmark::State& state) { - GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, - basic::R::integer::number1, state); +static void BM_AssetManagerGetResourceOld(benchmark::State& state, uint32_t resid) { + GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid, + state); } -BENCHMARK(BM_AssetManagerGetResourceOld); +BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, number1, basic::R::integer::number1); +BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, deep_ref, basic::R::integer::deep_ref); static void BM_AssetManagerGetLibraryResource(benchmark::State& state) { GetResourceBenchmark( @@ -196,7 +197,7 @@ BENCHMARK(BM_AssetManagerGetResourceLocales); static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) { AssetManager assets; if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/, - false /*isSystemAssets*/)) { + true /*isSystemAssets*/)) { state.SkipWithError("Failed to load assets"); return; } @@ -211,4 +212,44 @@ static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) { } BENCHMARK(BM_AssetManagerGetResourceLocalesOld); +static void BM_AssetManagerSetConfigurationFramework(benchmark::State& state) { + std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(kFrameworkPath); + if (apk == nullptr) { + state.SkipWithError("Failed to load assets"); + return; + } + + AssetManager2 assets; + assets.SetApkAssets({apk.get()}); + + ResTable_config config; + memset(&config, 0, sizeof(config)); + + while (state.KeepRunning()) { + config.sdkVersion = ~config.sdkVersion; + assets.SetConfiguration(config); + } +} +BENCHMARK(BM_AssetManagerSetConfigurationFramework); + +static void BM_AssetManagerSetConfigurationFrameworkOld(benchmark::State& state) { + AssetManager assets; + if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/, + true /*isSystemAssets*/)) { + state.SkipWithError("Failed to load assets"); + return; + } + + const ResTable& table = assets.getResources(true); + + ResTable_config config; + memset(&config, 0, sizeof(config)); + + while (state.KeepRunning()) { + config.sdkVersion = ~config.sdkVersion; + assets.setConfiguration(config); + } +} +BENCHMARK(BM_AssetManagerSetConfigurationFrameworkOld); + } // namespace android diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 92462a6cfadf..7cac2b3417b5 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -36,6 +36,10 @@ namespace lib_one = com::android::lib_one; namespace lib_two = com::android::lib_two; namespace libclient = com::android::libclient; +using ::testing::Eq; +using ::testing::NotNull; +using ::testing::StrEq; + namespace android { class AssetManager2Test : public ::testing::Test { @@ -59,11 +63,14 @@ class AssetManager2Test : public ::testing::Test { libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk"); ASSERT_NE(nullptr, libclient_assets_); - appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); + appaslib_assets_ = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk"); ASSERT_NE(nullptr, appaslib_assets_); system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/); ASSERT_NE(nullptr, system_assets_); + + app_assets_ = ApkAssets::Load(GetTestDataPath() + "/app/app.apk"); + ASSERT_THAT(app_assets_, NotNull()); } protected: @@ -75,6 +82,7 @@ class AssetManager2Test : public ::testing::Test { std::unique_ptr<const ApkAssets> libclient_assets_; std::unique_ptr<const ApkAssets> appaslib_assets_; std::unique_ptr<const ApkAssets> system_assets_; + std::unique_ptr<const ApkAssets> app_assets_; }; TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) { @@ -233,6 +241,25 @@ TEST_F(AssetManager2Test, FindsBagResourceFromSharedLibrary) { assetmanager.SetApkAssets( {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); + const ResolvedBag* bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03)); + ASSERT_NE(nullptr, bag); + ASSERT_GE(bag->entry_count, 2u); + + // First two attributes come from lib_one. + EXPECT_EQ(1, bag->entries[0].cookie); + EXPECT_EQ(0x03, get_package_id(bag->entries[0].key)); + EXPECT_EQ(1, bag->entries[1].cookie); + EXPECT_EQ(0x03, get_package_id(bag->entries[1].key)); +} + +TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) { + AssetManager2 assetmanager; + + // libclient is built with lib_one and then lib_two in order. + // Reverse the order to test that proper package ID re-assignment is happening. + assetmanager.SetApkAssets( + {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); + const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::Theme); ASSERT_NE(nullptr, bag); ASSERT_GE(bag->entry_count, 2u); @@ -446,8 +473,68 @@ TEST_F(AssetManager2Test, GetResourceId) { assetmanager.GetResourceId("main", "layout", "com.android.basic")); } -TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {} +TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({system_assets_.get()}); + + std::unique_ptr<Asset> asset = assetmanager.Open("file.txt", Asset::ACCESS_BUFFER); + ASSERT_THAT(asset, NotNull()); + + const char* data = reinterpret_cast<const char*>(asset->getBuffer(false /*wordAligned*/)); + ASSERT_THAT(data, NotNull()); + EXPECT_THAT(std::string(data, asset->getLength()), StrEq("file\n")); +} + +TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({system_assets_.get(), app_assets_.get()}); + + std::unique_ptr<Asset> asset = assetmanager.Open("file.txt", Asset::ACCESS_BUFFER); + ASSERT_THAT(asset, NotNull()); + + const char* data = reinterpret_cast<const char*>(asset->getBuffer(false /*wordAligned*/)); + ASSERT_THAT(data, NotNull()); + EXPECT_THAT(std::string(data, asset->getLength()), StrEq("app override file\n")); +} + +TEST_F(AssetManager2Test, OpenDir) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({system_assets_.get()}); + + std::unique_ptr<AssetDir> asset_dir = assetmanager.OpenDir(""); + ASSERT_THAT(asset_dir, NotNull()); + ASSERT_THAT(asset_dir->getFileCount(), Eq(2u)); + + EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("file.txt"))); + EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular)); -TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {} + EXPECT_THAT(asset_dir->getFileName(1), Eq(String8("subdir"))); + EXPECT_THAT(asset_dir->getFileType(1), Eq(FileType::kFileTypeDirectory)); + + asset_dir = assetmanager.OpenDir("subdir"); + ASSERT_THAT(asset_dir, NotNull()); + ASSERT_THAT(asset_dir->getFileCount(), Eq(1u)); + + EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("subdir_file.txt"))); + EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular)); +} + +TEST_F(AssetManager2Test, OpenDirFromManyApks) { + AssetManager2 assetmanager; + assetmanager.SetApkAssets({system_assets_.get(), app_assets_.get()}); + + std::unique_ptr<AssetDir> asset_dir = assetmanager.OpenDir(""); + ASSERT_THAT(asset_dir, NotNull()); + ASSERT_THAT(asset_dir->getFileCount(), Eq(3u)); + + EXPECT_THAT(asset_dir->getFileName(0), Eq(String8("app_file.txt"))); + EXPECT_THAT(asset_dir->getFileType(0), Eq(FileType::kFileTypeRegular)); + + EXPECT_THAT(asset_dir->getFileName(1), Eq(String8("file.txt"))); + EXPECT_THAT(asset_dir->getFileType(1), Eq(FileType::kFileTypeRegular)); + + EXPECT_THAT(asset_dir->getFileName(2), Eq(String8("subdir"))); + EXPECT_THAT(asset_dir->getFileType(2), Eq(FileType::kFileTypeDirectory)); +} } // namespace android diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp new file mode 100644 index 000000000000..fa300c50218a --- /dev/null +++ b/libs/androidfw/tests/AttributeResolution_bench.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "benchmark/benchmark.h" + +//#include "android-base/stringprintf.h" +#include "androidfw/ApkAssets.h" +#include "androidfw/AssetManager.h" +#include "androidfw/AssetManager2.h" +#include "androidfw/AttributeResolution.h" +#include "androidfw/ResourceTypes.h" + +#include "BenchmarkHelpers.h" +#include "data/basic/R.h" +#include "data/styles/R.h" + +namespace app = com::android::app; +namespace basic = com::android::basic; + +namespace android { + +constexpr const static char* kFrameworkPath = "/system/framework/framework-res.apk"; +constexpr const static uint32_t Theme_Material_Light = 0x01030237u; + +static void BM_ApplyStyle(benchmark::State& state) { + std::unique_ptr<const ApkAssets> styles_apk = + ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + if (styles_apk == nullptr) { + state.SkipWithError("failed to load assets"); + return; + } + + AssetManager2 assetmanager; + assetmanager.SetApkAssets({styles_apk.get()}); + + std::unique_ptr<Asset> asset = + assetmanager.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); + if (asset == nullptr) { + state.SkipWithError("failed to load layout"); + return; + } + + ResXMLTree xml_tree; + if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) { + state.SkipWithError("corrupt xml layout"); + return; + } + + // Skip to the first tag. + while (xml_tree.next() != ResXMLParser::START_TAG) { + } + + std::unique_ptr<Theme> theme = assetmanager.NewTheme(); + theme->ApplyStyle(app::R::style::StyleTwo); + + std::array<uint32_t, 6> attrs{{app::R::attr::attr_one, app::R::attr::attr_two, + app::R::attr::attr_three, app::R::attr::attr_four, + app::R::attr::attr_five, app::R::attr::attr_empty}}; + std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; + std::array<uint32_t, attrs.size() + 1> indices; + + while (state.KeepRunning()) { + ApplyStyle(theme.get(), &xml_tree, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), + attrs.size(), values.data(), indices.data()); + } +} +BENCHMARK(BM_ApplyStyle); + +static void BM_ApplyStyleFramework(benchmark::State& state) { + std::unique_ptr<const ApkAssets> framework_apk = ApkAssets::Load(kFrameworkPath); + if (framework_apk == nullptr) { + state.SkipWithError("failed to load framework assets"); + return; + } + + std::unique_ptr<const ApkAssets> basic_apk = + ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk"); + if (basic_apk == nullptr) { + state.SkipWithError("failed to load assets"); + return; + } + + AssetManager2 assetmanager; + assetmanager.SetApkAssets({framework_apk.get(), basic_apk.get()}); + + ResTable_config device_config; + memset(&device_config, 0, sizeof(device_config)); + device_config.language[0] = 'e'; + device_config.language[1] = 'n'; + device_config.country[0] = 'U'; + device_config.country[1] = 'S'; + device_config.orientation = ResTable_config::ORIENTATION_PORT; + device_config.smallestScreenWidthDp = 700; + device_config.screenWidthDp = 700; + device_config.screenHeightDp = 1024; + device_config.sdkVersion = 27; + + Res_value value; + ResTable_config config; + uint32_t flags = 0u; + ApkAssetsCookie cookie = + assetmanager.GetResource(basic::R::layout::layoutt, false /*may_be_bag*/, + 0u /*density_override*/, &value, &config, &flags); + if (cookie == kInvalidCookie) { + state.SkipWithError("failed to find R.layout.layout"); + return; + } + + size_t len = 0u; + const char* layout_path = + assetmanager.GetStringPoolForCookie(cookie)->string8At(value.data, &len); + if (layout_path == nullptr || len == 0u) { + state.SkipWithError("failed to lookup layout path"); + return; + } + + std::unique_ptr<Asset> asset = assetmanager.OpenNonAsset( + StringPiece(layout_path, len).to_string(), cookie, Asset::ACCESS_BUFFER); + if (asset == nullptr) { + state.SkipWithError("failed to load layout"); + return; + } + + ResXMLTree xml_tree; + if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) { + state.SkipWithError("corrupt xml layout"); + return; + } + + // Skip to the first tag. + while (xml_tree.next() != ResXMLParser::START_TAG) { + } + + std::unique_ptr<Theme> theme = assetmanager.NewTheme(); + theme->ApplyStyle(Theme_Material_Light); + + std::array<uint32_t, 92> attrs{ + {0x0101000e, 0x01010034, 0x01010095, 0x01010096, 0x01010097, 0x01010098, 0x01010099, + 0x0101009a, 0x0101009b, 0x010100ab, 0x010100af, 0x010100b0, 0x010100b1, 0x0101011f, + 0x01010120, 0x0101013f, 0x01010140, 0x0101014e, 0x0101014f, 0x01010150, 0x01010151, + 0x01010152, 0x01010153, 0x01010154, 0x01010155, 0x01010156, 0x01010157, 0x01010158, + 0x01010159, 0x0101015a, 0x0101015b, 0x0101015c, 0x0101015d, 0x0101015e, 0x0101015f, + 0x01010160, 0x01010161, 0x01010162, 0x01010163, 0x01010164, 0x01010165, 0x01010166, + 0x01010167, 0x01010168, 0x01010169, 0x0101016a, 0x0101016b, 0x0101016c, 0x0101016d, + 0x0101016e, 0x0101016f, 0x01010170, 0x01010171, 0x01010217, 0x01010218, 0x0101021d, + 0x01010220, 0x01010223, 0x01010224, 0x01010264, 0x01010265, 0x01010266, 0x010102c5, + 0x010102c6, 0x010102c7, 0x01010314, 0x01010315, 0x01010316, 0x0101035e, 0x0101035f, + 0x01010362, 0x01010374, 0x0101038c, 0x01010392, 0x01010393, 0x010103ac, 0x0101045d, + 0x010104b6, 0x010104b7, 0x010104d6, 0x010104d7, 0x010104dd, 0x010104de, 0x010104df, + 0x01010535, 0x01010536, 0x01010537, 0x01010538, 0x01010546, 0x01010567, 0x011100c9, + 0x011100ca}}; + + std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; + std::array<uint32_t, attrs.size() + 1> indices; + while (state.KeepRunning()) { + ApplyStyle(theme.get(), &xml_tree, 0x01010084u /*def_style_attr*/, 0u /*def_style_res*/, + attrs.data(), attrs.size(), values.data(), indices.data()); + } +} +BENCHMARK(BM_ApplyStyleFramework); + +} // namespace android diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index 2d73ce8f8ee3..c8dbe205fee2 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -21,6 +21,8 @@ #include "android-base/file.h" #include "android-base/logging.h" #include "android-base/macros.h" +#include "androidfw/AssetManager2.h" +#include "androidfw/ResourceUtils.h" #include "TestHelpers.h" #include "data/styles/R.h" @@ -32,15 +34,14 @@ namespace android { class AttributeResolutionTest : public ::testing::Test { public: virtual void SetUp() override { - std::string contents; - ASSERT_TRUE(ReadFileFromZipToString( - GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, table_.add(contents.data(), contents.size(), - 1 /*cookie*/, true /*copyData*/)); + styles_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk"); + ASSERT_NE(nullptr, styles_assets_); + assetmanager_.SetApkAssets({styles_assets_.get()}); } protected: - ResTable table_; + std::unique_ptr<const ApkAssets> styles_assets_; + AssetManager2 assetmanager_; }; class AttributeResolutionXmlTest : public AttributeResolutionTest { @@ -48,13 +49,12 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { virtual void SetUp() override { AttributeResolutionTest::SetUp(); - std::string contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", - "res/layout/layout.xml", &contents)); + std::unique_ptr<Asset> asset = + assetmanager_.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER); + ASSERT_NE(nullptr, asset); - ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(), - true /*copyData*/)); + ASSERT_EQ(NO_ERROR, + xml_parser_.setTo(asset->getBuffer(true), asset->getLength(), true /*copyData*/)); // Skip to the first tag. while (xml_parser_.next() != ResXMLParser::START_TAG) { @@ -65,15 +65,50 @@ class AttributeResolutionXmlTest : public AttributeResolutionTest { ResXMLTree xml_parser_; }; +TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) { + AssetManager2 assetmanager; + auto apk_assets = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/styles/styles.apk"); + ASSERT_NE(nullptr, apk_assets); + assetmanager.SetApkAssets({apk_assets.get()}); + + std::unique_ptr<Theme> theme = assetmanager.NewTheme(); + + std::array<uint32_t, 2> attrs{ + {fix_package_id(R::attr::attr_one, 0x02), fix_package_id(R::attr::attr_two, 0x02)}}; + std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; + std::array<uint32_t, attrs.size() + 1> indices; + ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/, + fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(), values.data(), + indices.data()); + + const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; + + const uint32_t* values_cursor = values.data(); + EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]); + EXPECT_EQ(1u, values_cursor[STYLE_DATA]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); + + values_cursor += STYLE_NUM_ENTRIES; + EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]); + EXPECT_EQ(2u, values_cursor[STYLE_DATA]); + EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]); + EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]); + EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]); + EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]); +} + TEST_F(AttributeResolutionTest, Theme) { - ResTable::Theme theme(table_); - ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); + std::unique_ptr<Theme> theme = assetmanager_.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_empty}}; std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; - ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/, + ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/, nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(), attrs.size(), values.data(), nullptr /*out_indices*/)); @@ -126,8 +161,8 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { R::attr::attr_four, R::attr::attr_empty}}; std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; - ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(), - nullptr /*out_indices*/)); + ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(), + values.data(), nullptr /*out_indices*/)); uint32_t* values_cursor = values.data(); EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]); @@ -171,15 +206,15 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { } TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) { - ResTable::Theme theme(table_); - ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo)); + std::unique_ptr<Theme> theme = assetmanager_.NewTheme(); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array<uint32_t, 6> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}}; std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; std::array<uint32_t, attrs.size() + 1> indices; - ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(), + ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), attrs.size(), values.data(), indices.data()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index 7149beef797f..faddfe599af4 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -33,19 +33,21 @@ void GetResourceBenchmarkOld(const std::vector<std::string>& paths, const ResTab } } + // Make sure to force creation of the ResTable first, or else the configuration doesn't get set. + const ResTable& table = assetmanager.getResources(true); if (config != nullptr) { assetmanager.setConfiguration(*config); } - const ResTable& table = assetmanager.getResources(true); - Res_value value; ResTable_config selected_config; uint32_t flags; + uint32_t last_ref = 0u; while (state.KeepRunning()) { - table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags, - &selected_config); + ssize_t block = table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags, + &selected_config); + table.resolveReference(&value, block, &last_ref, &flags, &selected_config); } } @@ -72,10 +74,12 @@ void GetResourceBenchmark(const std::vector<std::string>& paths, const ResTable_ Res_value value; ResTable_config selected_config; uint32_t flags; + uint32_t last_id = 0u; while (state.KeepRunning()) { - assetmanager.GetResource(resid, false /* may_be_bag */, 0u /* density_override */, &value, - &selected_config, &flags); + ApkAssetsCookie cookie = assetmanager.GetResource( + resid, false /* may_be_bag */, 0u /* density_override */, &value, &selected_config, &flags); + assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_id); } } diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 37ddafb14fd3..cae632ddea30 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -16,6 +16,9 @@ #include "androidfw/LoadedArsc.h" +#include "android-base/file.h" +#include "androidfw/ResourceUtils.h" + #include "TestHelpers.h" #include "data/basic/R.h" #include "data/libclient/R.h" @@ -27,6 +30,14 @@ namespace basic = com::android::basic; namespace libclient = com::android::libclient; namespace sparse = com::android::sparse; +using ::android::base::ReadFileToString; +using ::testing::Eq; +using ::testing::Ge; +using ::testing::IsNull; +using ::testing::NotNull; +using ::testing::SizeIs; +using ::testing::StrEq; + namespace android { TEST(LoadedArscTest, LoadSinglePackageArsc) { @@ -35,39 +46,24 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { &contents)); std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); - - const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - EXPECT_EQ(std::string("com.android.app"), packages[0]->GetPackageName()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); - - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.sdkVersion = 24; + ASSERT_THAT(loaded_arsc, NotNull()); - FindEntryResult entry; - - ASSERT_TRUE(loaded_arsc->FindEntry(app::R::string::string_one, config, &entry)); - ASSERT_NE(nullptr, entry.entry); -} - -TEST(LoadedArscTest, FindDefaultEntry) { - std::string contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(app::R::string::string_one)); + ASSERT_THAT(package, NotNull()); + EXPECT_THAT(package->GetPackageName(), StrEq("com.android.app")); + EXPECT_THAT(package->GetPackageId(), Eq(0x7f)); - std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + const uint8_t type_index = get_type_id(app::R::string::string_one) - 1; + const uint16_t entry_index = get_entry_id(app::R::string::string_one); - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); - desired_config.language[0] = 'd'; - desired_config.language[1] = 'e'; + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry)); - ASSERT_NE(nullptr, entry.entry); + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); } TEST(LoadedArscTest, LoadSparseEntryApp) { @@ -76,15 +72,22 @@ TEST(LoadedArscTest, LoadSparseEntryApp) { &contents)); std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); + + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9)); + ASSERT_THAT(package, NotNull()); + + const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1; + const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9); - ResTable_config config; - memset(&config, 0, sizeof(config)); - config.sdkVersion = 26; + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(sparse::R::integer::foo_9, config, &entry)); - ASSERT_NE(nullptr, entry.entry); + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); } TEST(LoadedArscTest, LoadSharedLibrary) { @@ -93,14 +96,13 @@ TEST(LoadedArscTest, LoadSharedLibrary) { &contents)); std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_EQ(std::string("com.android.lib_one"), packages[0]->GetPackageName()); - EXPECT_EQ(0, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.lib_one")); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0)); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); @@ -114,25 +116,23 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) { "resources.arsc", &contents)); std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_FALSE(packages[0]->IsDynamic()); - EXPECT_EQ(std::string("com.android.libclient"), packages[0]->GetPackageName()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.libclient")); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap(); // The library has two dependencies. - ASSERT_EQ(2u, dynamic_pkg_map.size()); + ASSERT_THAT(dynamic_pkg_map, SizeIs(2u)); + EXPECT_THAT(dynamic_pkg_map[0].package_name, StrEq("com.android.lib_one")); + EXPECT_THAT(dynamic_pkg_map[0].package_id, Eq(0x02)); - EXPECT_EQ(std::string("com.android.lib_one"), dynamic_pkg_map[0].package_name); - EXPECT_EQ(0x02, dynamic_pkg_map[0].package_id); - - EXPECT_EQ(std::string("com.android.lib_two"), dynamic_pkg_map[1].package_name); - EXPECT_EQ(0x03, dynamic_pkg_map[1].package_id); + EXPECT_THAT(dynamic_pkg_map[1].package_name, StrEq("com.android.lib_two")); + EXPECT_THAT(dynamic_pkg_map[1].package_id, Eq(0x03)); } TEST(LoadedArscTest, LoadAppAsSharedLibrary) { @@ -143,13 +143,12 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/, true /*load_as_shared_library*/); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); - ASSERT_EQ(1u, packages.size()); - + ASSERT_THAT(packages, SizeIs(1u)); EXPECT_TRUE(packages[0]->IsDynamic()); - EXPECT_EQ(0x7f, packages[0]->GetPackageId()); + EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f)); } TEST(LoadedArscTest, LoadFeatureSplit) { @@ -157,21 +156,67 @@ TEST(LoadedArscTest, LoadFeatureSplit) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc", &contents)); std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); - ASSERT_NE(nullptr, loaded_arsc); + ASSERT_THAT(loaded_arsc, NotNull()); - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); + const LoadedPackage* package = + loaded_arsc->GetPackageById(get_package_id(basic::R::string::test3)); + ASSERT_THAT(package, NotNull()); - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry)); + uint8_t type_index = get_type_id(basic::R::string::test3) - 1; + uint8_t entry_index = get_entry_id(basic::R::string::test3); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); size_t len; - const char16_t* type_name16 = entry.type_string_ref.string16(&len); - ASSERT_NE(nullptr, type_name16); - ASSERT_NE(0u, len); + const char16_t* type_name16 = + package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len); + ASSERT_THAT(type_name16, NotNull()); + EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string")); - std::string type_name = util::Utf16ToUtf8(StringPiece16(type_name16, len)); - EXPECT_EQ(std::string("string"), type_name); + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull()); +} + +// AAPT(2) generates resource tables with chunks in a certain order. The rule is that +// a RES_TABLE_TYPE_TYPE with id `i` must always be preceded by a RES_TABLE_TYPE_SPEC_TYPE with +// id `i`. The RES_TABLE_TYPE_SPEC_TYPE does not need to be directly preceding, however. +// +// AAPT(2) generates something like: +// RES_TABLE_TYPE_SPEC_TYPE id=1 +// RES_TABLE_TYPE_TYPE id=1 +// RES_TABLE_TYPE_SPEC_TYPE id=2 +// RES_TABLE_TYPE_TYPE id=2 +// +// But the following is valid too: +// RES_TABLE_TYPE_SPEC_TYPE id=1 +// RES_TABLE_TYPE_SPEC_TYPE id=2 +// RES_TABLE_TYPE_TYPE id=1 +// RES_TABLE_TYPE_TYPE id=2 +// +TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) { + std::string contents; + ASSERT_TRUE( + ReadFileFromZipToString(GetTestDataPath() + "/out_of_order_types/out_of_order_types.apk", + "resources.arsc", &contents)); + + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); + ASSERT_THAT(loaded_arsc, NotNull()); + + ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); + const auto& package = loaded_arsc->GetPackages()[0]; + ASSERT_THAT(package, NotNull()); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); + + type_spec = package->GetTypeSpecByTypeIndex(1); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); } class MockLoadedIdmap : public LoadedIdmap { @@ -199,23 +244,33 @@ class MockLoadedIdmap : public LoadedIdmap { }; TEST(LoadedArscTest, LoadOverlay) { - std::string contents, overlay_contents; - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); + std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc", - &overlay_contents)); + &contents)); MockLoadedIdmap loaded_idmap; std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(StringPiece(overlay_contents), &loaded_idmap); - ASSERT_NE(nullptr, loaded_arsc); - - ResTable_config desired_config; - memset(&desired_config, 0, sizeof(desired_config)); - - FindEntryResult entry; - ASSERT_TRUE(loaded_arsc->FindEntry(0x08030001u, desired_config, &entry)); + LoadedArsc::Load(StringPiece(contents), &loaded_idmap); + ASSERT_THAT(loaded_arsc, NotNull()); + + const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u); + ASSERT_THAT(package, NotNull()); + + const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1); + ASSERT_THAT(type_spec, NotNull()); + ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); + + // The entry being overlaid doesn't exist at the original entry index. + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull()); + + // Since this is an overlay, the actual entry ID must be mapped. + ASSERT_THAT(type_spec->idmap_entries, NotNull()); + uint16_t target_entry_id = 0u; + ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id)); + ASSERT_THAT(target_entry_id, Eq(0x0u)); + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull()); } // structs with size fields (like Res_value, ResTable_entry) should be diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h index 43a995536d89..df0c642f4565 100644 --- a/libs/androidfw/tests/TestHelpers.h +++ b/libs/androidfw/tests/TestHelpers.h @@ -20,6 +20,7 @@ #include <string> #include "androidfw/ResourceTypes.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "CommonHelpers.h" diff --git a/libs/androidfw/tests/data/app/app.apk b/libs/androidfw/tests/data/app/app.apk Binary files differindex ccb08242a656..c8ad86ded851 100644 --- a/libs/androidfw/tests/data/app/app.apk +++ b/libs/androidfw/tests/data/app/app.apk diff --git a/libs/androidfw/tests/data/app/assets/app_file.txt b/libs/androidfw/tests/data/app/assets/app_file.txt new file mode 100644 index 000000000000..b214e06d6ece --- /dev/null +++ b/libs/androidfw/tests/data/app/assets/app_file.txt @@ -0,0 +1 @@ +app file diff --git a/libs/androidfw/tests/data/app/assets/file.txt b/libs/androidfw/tests/data/app/assets/file.txt new file mode 100644 index 000000000000..081154272520 --- /dev/null +++ b/libs/androidfw/tests/data/app/assets/file.txt @@ -0,0 +1 @@ +app override file diff --git a/libs/androidfw/tests/data/app/build b/libs/androidfw/tests/data/app/build index d418158c547b..09af842e70fb 100755 --- a/libs/androidfw/tests/data/app/build +++ b/libs/androidfw/tests/data/app/build @@ -17,4 +17,11 @@ set -e -aapt package -I ../system/system.apk -M AndroidManifest.xml -S res -F app.apk -f +aapt2 compile --dir res -o compiled.flata +aapt2 link \ + --manifest AndroidManifest.xml \ + -I ../system/system.apk \ + -A assets \ + -o app.apk \ + compiled.flata +rm compiled.flata diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h index 94a2a14ced87..b7e814fea079 100644 --- a/libs/androidfw/tests/data/basic/R.h +++ b/libs/androidfw/tests/data/basic/R.h @@ -34,6 +34,7 @@ struct R { struct layout { enum : uint32_t { main = 0x7f020000, + layoutt = 0x7f020001, }; }; @@ -55,6 +56,7 @@ struct R { number2 = 0x7f040001, ref1 = 0x7f040002, ref2 = 0x7f040003, + deep_ref = 0x7f040004, // From feature number3 = 0x80030000, diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk Binary files differindex 18ef75e91ded..1733b6a16546 100644 --- a/libs/androidfw/tests/data/basic/basic.apk +++ b/libs/androidfw/tests/data/basic/basic.apk diff --git a/libs/androidfw/tests/data/basic/res/layout/layout.xml b/libs/androidfw/tests/data/basic/res/layout/layout.xml new file mode 100644 index 000000000000..045ede454bca --- /dev/null +++ b/libs/androidfw/tests/data/basic/res/layout/layout.xml @@ -0,0 +1,25 @@ +<?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. +--> +<Button xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/ok" + android:layout_width="0sp" + android:layout_height="fill_parent" + android:layout_weight="1" + android:layout_marginStart="2dip" + android:layout_marginEnd="2dip" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textStyle="bold" + android:text="@android:string/ok" />
\ No newline at end of file diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml index 6c474596b5cd..b3435629265b 100644 --- a/libs/androidfw/tests/data/basic/res/values/values.xml +++ b/libs/androidfw/tests/data/basic/res/values/values.xml @@ -22,6 +22,7 @@ <attr name="attr2" format="reference|integer" /> <public type="layout" name="main" id="0x7f020000" /> + <public type="layout" name="layout" id="0x7f020001" /> <public type="string" name="test1" id="0x7f030000" /> <string name="test1">test1</string> @@ -43,6 +44,18 @@ <public type="integer" name="ref2" id="0x7f040003" /> <integer name="ref2">12000</integer> + <public type="integer" name="deep_ref" id="0x7f040004" /> + <integer name="deep_ref">@integer/deep_ref_1</integer> + <integer name="deep_ref_1">@integer/deep_ref_2</integer> + <integer name="deep_ref_2">@integer/deep_ref_3</integer> + <integer name="deep_ref_3">@integer/deep_ref_4</integer> + <integer name="deep_ref_4">@integer/deep_ref_5</integer> + <integer name="deep_ref_5">@integer/deep_ref_6</integer> + <integer name="deep_ref_6">@integer/deep_ref_7</integer> + <integer name="deep_ref_7">@integer/deep_ref_8</integer> + <integer name="deep_ref_8">@integer/deep_ref_9</integer> + <integer name="deep_ref_9">100</integer> + <public type="style" name="Theme1" id="0x7f050000" /> <style name="Theme1"> <item name="com.android.basic:attr1">100</item> diff --git a/libs/androidfw/tests/data/out_of_order_types/AndroidManifest.xml b/libs/androidfw/tests/data/out_of_order_types/AndroidManifest.xml new file mode 100644 index 000000000000..34016db8b808 --- /dev/null +++ b/libs/androidfw/tests/data/out_of_order_types/AndroidManifest.xml @@ -0,0 +1,18 @@ +<?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.app" /> diff --git a/libs/androidfw/tests/data/out_of_order_types/build b/libs/androidfw/tests/data/out_of_order_types/build new file mode 100755 index 000000000000..8496f81038b0 --- /dev/null +++ b/libs/androidfw/tests/data/out_of_order_types/build @@ -0,0 +1,22 @@ +#!/bin/bash +# +# 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. +# + +set -e + +aapt2 compile --dir res -o compiled.flata +aapt2 link --manifest AndroidManifest.xml -o out_of_order_types.apk compiled.flata +rm compiled.flata diff --git a/libs/androidfw/tests/data/out_of_order_types/edited_resources.arsc.txt b/libs/androidfw/tests/data/out_of_order_types/edited_resources.arsc.txt new file mode 100644 index 000000000000..eca8f478c501 --- /dev/null +++ b/libs/androidfw/tests/data/out_of_order_types/edited_resources.arsc.txt @@ -0,0 +1,43 @@ +00000000: 0200 0c00 ac02 0000 0100 0000 0100 1c00 ................ +00000010: 1c00 0000 0000 0000 0000 0000 0001 0000 ................ +00000020: 1c00 0000 0000 0000 0002 2001 8402 0000 .......... ..... +00000030: 7f00 0000 6300 6f00 6d00 2e00 6100 6e00 ....c.o.m...a.n. +00000040: 6400 7200 6f00 6900 6400 2e00 6100 7000 d.r.o.i.d...a.p. +00000050: 7000 0000 0000 0000 0000 0000 0000 0000 p............... +00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000090: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +000000a0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +000000b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +000000c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +000000d0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +000000e0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +000000f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000100: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000110: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000120: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000130: 0000 0000 2001 0000 0000 0000 6401 0000 .... .......d... +00000140: 0000 0000 0000 0000 0100 1c00 4400 0000 ............D... +00000150: 0200 0000 0000 0000 0000 0000 2400 0000 ............$... +00000160: 0000 0000 0000 0000 0c00 0000 0400 6200 ..............b. +00000170: 6f00 6f00 6c00 0000 0700 6900 6e00 7400 o.o.l.....i.n.t. +00000180: 6500 6700 6500 7200 0000 0000 0100 1c00 e.g.e.r......... +00000190: 2800 0000 0100 0000 0000 0000 0001 0000 (............... +000001a0: 2000 0000 0000 0000 0000 0000 0404 7465 .............te +000001b0: 7374 0000 0202 1000 1400 0000 0100 0000 st.............. +000001c0: 0100 0000 0000 0000 0202 1000 1400 0000 +000001d0: 0200 0000 0100 0000 0000 0000 0102 5400 +000001e0: 6800 0000 0100 0000 0100 0000 5800 0000 +000001f0: 4000 0000 0000 0000 0000 0000 0000 0000 +00000200: 0000 0000 0000 0000 0000 0000 0000 0000 +00000210: 0000 0000 0000 0000 0000 0000 0000 0000 +00000220: 0000 0000 0000 0000 0000 0000 0000 0000 +00000230: 0000 0000 0800 0000 0000 0000 0800 0012 +00000240: ffff ffff 0102 5400 6800 0000 0200 0000 ......T.h....... +00000250: 0100 0000 5800 0000 4000 0000 0000 0000 ....X...@....... +00000260: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000270: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000280: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000290: 0000 0000 0000 0000 0000 0000 0800 0000 ................ +000002a0: 0000 0000 0800 0010 0100 0000 ............ diff --git a/libs/androidfw/tests/data/out_of_order_types/out_of_order_types.apk b/libs/androidfw/tests/data/out_of_order_types/out_of_order_types.apk Binary files differnew file mode 100644 index 000000000000..75146e0fc476 --- /dev/null +++ b/libs/androidfw/tests/data/out_of_order_types/out_of_order_types.apk diff --git a/libs/androidfw/tests/data/out_of_order_types/res/values/values.xml b/libs/androidfw/tests/data/out_of_order_types/res/values/values.xml new file mode 100644 index 000000000000..7c54fbae9f21 --- /dev/null +++ b/libs/androidfw/tests/data/out_of_order_types/res/values/values.xml @@ -0,0 +1,20 @@ +<?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. +--> + +<resources> + <bool name="test">true</bool> + <integer name="test">1</integer> +</resources> diff --git a/libs/androidfw/tests/data/system/assets/file.txt b/libs/androidfw/tests/data/system/assets/file.txt new file mode 100644 index 000000000000..f73f3093ff86 --- /dev/null +++ b/libs/androidfw/tests/data/system/assets/file.txt @@ -0,0 +1 @@ +file diff --git a/libs/androidfw/tests/data/system/assets/subdir/subdir_file.txt b/libs/androidfw/tests/data/system/assets/subdir/subdir_file.txt new file mode 100644 index 000000000000..3f74eb6e6441 --- /dev/null +++ b/libs/androidfw/tests/data/system/assets/subdir/subdir_file.txt @@ -0,0 +1 @@ +subdir file diff --git a/libs/androidfw/tests/data/system/build b/libs/androidfw/tests/data/system/build index bfbdf4ca770b..b65145a8454f 100755 --- a/libs/androidfw/tests/data/system/build +++ b/libs/androidfw/tests/data/system/build @@ -17,4 +17,6 @@ set -e -aapt package -x -M AndroidManifest.xml -S res -F system.apk -f +aapt2 compile --dir res -o compiled.flata +aapt2 link --manifest AndroidManifest.xml -A assets -o system.apk compiled.flata +rm compiled.flata diff --git a/libs/androidfw/tests/data/system/res/values-sv/values.xml b/libs/androidfw/tests/data/system/res/values-sv/values.xml index b97bdb68aca7..5f60d214c744 100644 --- a/libs/androidfw/tests/data/system/res/values-sv/values.xml +++ b/libs/androidfw/tests/data/system/res/values-sv/values.xml @@ -15,6 +15,5 @@ --> <resources> - <public type="integer" name="number" id="0x01030000" /> <integer name="number">1</integer> </resources> diff --git a/libs/androidfw/tests/data/system/res/values/themes.xml b/libs/androidfw/tests/data/system/res/values/themes.xml index 35d43c77fc7a..7893c946e299 100644 --- a/libs/androidfw/tests/data/system/res/values/themes.xml +++ b/libs/androidfw/tests/data/system/res/values/themes.xml @@ -18,6 +18,7 @@ <public name="background" type="attr" id="0x01010000"/> <public name="foreground" type="attr" id="0x01010001"/> <public name="Theme.One" type="style" id="0x01020000"/> + <public type="integer" name="number" id="0x01030000" /> <attr name="background" format="color|reference"/> <attr name="foreground" format="color|reference"/> diff --git a/libs/androidfw/tests/data/system/system.apk b/libs/androidfw/tests/data/system/system.apk Binary files differindex 1299016a0f83..9045d6c4de21 100644 --- a/libs/androidfw/tests/data/system/system.apk +++ b/libs/androidfw/tests/data/system/system.apk diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 24d819e93ff2..35790b6558c7 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -6,6 +6,7 @@ cc_defaults { //"hwui_bugreport_font_cache_usage", //"hwui_compile_for_perf", "hwui_pgo", + "hwui_lto", ], cpp_std: "c++17", @@ -126,6 +127,18 @@ cc_defaults { }, } +// Build hwui library with ThinLTO by default. +cc_defaults { + name: "hwui_lto", + target: { + android: { + lto: { + thin: true, + }, + }, + }, +} + // ------------------------ // library // ------------------------ diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 6cd283a9063c..e9adbdc1447b 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -62,6 +62,7 @@ bool Properties::skpCaptureEnabled = false; bool Properties::enableRTAnimations = true; bool Properties::runningInEmulator = false; +bool Properties::debuggingEnabled = false; static int property_get_int(const char* key, int defaultValue) { char buf[PROPERTY_VALUE_MAX] = { @@ -134,8 +135,7 @@ bool Properties::load() { filterOutTestOverhead = property_get_bool(PROPERTY_FILTER_TEST_OVERHEAD, false); - skpCaptureEnabled = property_get_bool("ro.debuggable", false) && - property_get_bool(PROPERTY_CAPTURE_SKP_ENABLED, false); + skpCaptureEnabled = debuggingEnabled && property_get_bool(PROPERTY_CAPTURE_SKP_ENABLED, false); runningInEmulator = property_get_bool(PROPERTY_QEMU_KERNEL, false); diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 179b97bf6d9f..3f44c211f024 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -268,6 +268,8 @@ public: static bool runningInEmulator; + ANDROID_API static bool debuggingEnabled; + private: static ProfileType sProfileType; static bool sDisableProfileBars; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 4be7a57daedf..c6a9b55f8ac1 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -356,14 +356,21 @@ void RenderProxy::disableVsync() { void RenderProxy::repackVectorDrawableAtlas() { RenderThread& thread = RenderThread::getInstance(); thread.queue().post([&thread]() { - thread.cacheManager().acquireVectorDrawableAtlas()->repackIfNeeded(thread.getGrContext()); + // The context may be null if trimMemory executed, but then the atlas was deleted too. + if (thread.getGrContext() != nullptr) { + thread.cacheManager().acquireVectorDrawableAtlas()->repackIfNeeded( + thread.getGrContext()); + } }); } void RenderProxy::releaseVDAtlasEntries() { RenderThread& thread = RenderThread::getInstance(); thread.queue().post([&thread]() { - thread.cacheManager().acquireVectorDrawableAtlas()->delayedReleaseEntries(); + // The context may be null if trimMemory executed, but then the atlas was deleted too. + if (thread.getGrContext() != nullptr) { + thread.cacheManager().acquireVectorDrawableAtlas()->delayedReleaseEntries(); + } }); } diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index 279e05f57d1c..e0c567b6212a 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -628,7 +628,8 @@ public final class MediaDrm implements AutoCloseable { } /** - * Open a new session with the MediaDrm object. A session ID is returned. + * Open a new session with the MediaDrm object. A session ID is returned. + * By default, sessions are opened at the native security level of the device. * * @throws NotProvisionedException if provisioning is needed * @throws ResourceBusyException if required resources are in use diff --git a/media/java/android/media/MediaMetadata2.java b/media/java/android/media/MediaMetadata2.java index f3425e81da4c..07367bb82df8 100644 --- a/media/java/android/media/MediaMetadata2.java +++ b/media/java/android/media/MediaMetadata2.java @@ -40,7 +40,8 @@ import java.util.Set; // TODO(jaewan): Add @see for APIs from MediaDescription public final class MediaMetadata2 { /** - * The title of the media. + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about the title of the media. * * @see Builder#putText(String, CharSequence) * @see Builder#putString(String, String) @@ -50,7 +51,8 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE"; /** - * The artist of the media. + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about the artist of the media. * * @see Builder#putText(String, CharSequence) * @see Builder#putString(String, String) @@ -60,8 +62,9 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST"; /** - * The duration of the media in ms. A negative duration indicates that the - * duration is unknown (or infinite). + * The metadata key for a {@link Long} typed value to retrieve the information about the + * duration of the media in ms. A negative duration indicates that the duration is unknown + * (or infinite). * * @see Builder#putLong(String, long) * @see #getLong(String) @@ -69,7 +72,8 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION"; /** - * The album title for the media. + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about the album title for the media. * * @see Builder#putText(String, CharSequence) * @see Builder#putString(String, String) @@ -79,7 +83,8 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM"; /** - * The author of the media. + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about the author of the media. * * @see Builder#putText(String, CharSequence) * @see Builder#putString(String, String) @@ -89,7 +94,8 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR"; /** - * The writer of the media. + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about the writer of the media. * * @see Builder#putText(String, CharSequence) * @see Builder#putString(String, String) @@ -99,7 +105,8 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER"; /** - * The composer of the media. + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about the composer of the media. * * @see Builder#putText(String, CharSequence) * @see Builder#putString(String, String) @@ -109,7 +116,8 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER"; /** - * The compilation status of the media. + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about the compilation status of the media. * * @see Builder#putText(String, CharSequence) * @see Builder#putString(String, String) @@ -119,8 +127,9 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION"; /** - * The date the media was created or published. The format is unspecified - * but RFC 3339 is recommended. + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about the date the media was created or published. + * The format is unspecified but RFC 3339 is recommended. * * @see Builder#putText(String, CharSequence) * @see Builder#putString(String, String) @@ -130,7 +139,8 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_DATE = "android.media.metadata.DATE"; /** - * The year the media was created or published as a long. + * The metadata key for a {@link Long} typed value to retrieve the information about the year + * the media was created or published. * * @see Builder#putLong(String, long) * @see #getLong(String) @@ -138,7 +148,8 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR"; /** - * The genre of the media. + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about the genre of the media. * * @see Builder#putText(String, CharSequence) * @see Builder#putString(String, String) @@ -148,7 +159,8 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE"; /** - * The track number for the media. + * The metadata key for a {@link Long} typed value to retrieve the information about the + * track number for the media. * * @see Builder#putLong(String, long) * @see #getLong(String) @@ -156,7 +168,8 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER"; /** - * The number of tracks in the media's original source. + * The metadata key for a {@link Long} typed value to retrieve the information about the + * number of tracks in the media's original source. * * @see Builder#putLong(String, long) * @see #getLong(String) @@ -164,7 +177,8 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS"; /** - * The disc number for the media's original source. + * The metadata key for a {@link Long} typed value to retrieve the information about the + * disc number for the media's original source. * * @see Builder#putLong(String, long) * @see #getLong(String) @@ -172,7 +186,8 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER"; /** - * The artist for the album of the media's original source. + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about the artist for the album of the media's original source. * * @see Builder#putText(String, CharSequence) * @see Builder#putString(String, String) @@ -182,11 +197,10 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST"; /** - * The artwork for the media as a {@link Bitmap}. - * - * The artwork should be relatively small and may be scaled down - * if it is too large. For higher resolution artwork - * {@link #METADATA_KEY_ART_URI} should be used instead. + * The metadata key for a {@link Bitmap} typed value to retrieve the information about the + * artwork for the media. + * The artwork should be relatively small and may be scaled down if it is too large. + * For higher resolution artwork, {@link #METADATA_KEY_ART_URI} should be used instead. * * @see Builder#putBitmap(String, Bitmap) * @see #getBitmap(String) @@ -194,7 +208,8 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_ART = "android.media.metadata.ART"; /** - * The artwork for the media as a Uri style String. + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about Uri of the artwork for the media. * * @see Builder#putText(String, CharSequence) * @see Builder#putString(String, String) @@ -204,11 +219,10 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI"; /** - * The artwork for the album of the media's original source as a - * {@link Bitmap}. - * The artwork should be relatively small and may be scaled down - * if it is too large. For higher resolution artwork - * {@link #METADATA_KEY_ALBUM_ART_URI} should be used instead. + * The metadata key for a {@link Bitmap} typed value to retrieve the information about the + * artwork for the album of the media's original source. + * The artwork should be relatively small and may be scaled down if it is too large. + * For higher resolution artwork, {@link #METADATA_KEY_ALBUM_ART_URI} should be used instead. * * @see Builder#putBitmap(String, Bitmap) * @see #getBitmap(String) @@ -216,8 +230,8 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART"; /** - * The artwork for the album of the media's original source as a Uri style - * String. + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about the Uri of the artwork for the album of the media's original source. * * @see Builder#putText(String, CharSequence) * @see Builder#putString(String, String) @@ -227,7 +241,8 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI"; /** - * The user's rating for the media. + * The metadata key for a {@link Rating2} typed value to retrieve the information about the + * user's rating for the media. * * @see Builder#putRating(String, Rating2) * @see #getRating(String) @@ -235,7 +250,8 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING"; /** - * The overall rating for the media. + * The metadata key for a {@link Rating2} typed value to retrieve the information about the + * overall rating for the media. * * @see Builder#putRating(String, Rating2) * @see #getRating(String) @@ -243,10 +259,10 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_RATING = "android.media.metadata.RATING"; /** - * A title that is suitable for display to the user. This will generally be - * the same as {@link #METADATA_KEY_TITLE} but may differ for some formats. - * When displaying media described by this metadata this should be preferred - * if present. + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about the title that is suitable for display to the user. + * It will generally be the same as {@link #METADATA_KEY_TITLE} but may differ for some formats. + * When displaying media described by this metadata, this should be preferred if present. * * @see Builder#putText(String, CharSequence) * @see Builder#putString(String, String) @@ -256,8 +272,9 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE"; /** - * A subtitle that is suitable for display to the user. When displaying a - * second line for media described by this metadata this should be preferred + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about the subtitle that is suitable for display to the user. + * When displaying a second line for media described by this metadata, this should be preferred * to other fields if present. * * @see Builder#putText(String, CharSequence) @@ -269,9 +286,10 @@ public final class MediaMetadata2 { = "android.media.metadata.DISPLAY_SUBTITLE"; /** - * A description that is suitable for display to the user. When displaying - * more information for media described by this metadata this should be - * preferred to other fields if present. + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about the description that is suitable for display to the user. + * When displaying more information for media described by this metadata, + * this should be preferred to other fields if present. * * @see Builder#putText(String, CharSequence) * @see Builder#putString(String, String) @@ -282,25 +300,24 @@ public final class MediaMetadata2 { = "android.media.metadata.DISPLAY_DESCRIPTION"; /** - * An icon or thumbnail that is suitable for display to the user. When - * displaying an icon for media described by this metadata this should be - * preferred to other fields if present. This must be a {@link Bitmap}. - * - * The icon should be relatively small and may be scaled down - * if it is too large. For higher resolution artwork - * {@link #METADATA_KEY_DISPLAY_ICON_URI} should be used instead. + * The metadata key for a {@link Bitmap} typed value to retrieve the information about the icon + * or thumbnail that is suitable for display to the user. + * When displaying an icon for media described by this metadata, this should be preferred to + * other fields if present. + * <p> + * The icon should be relatively small and may be scaled down if it is too large. + * For higher resolution artwork, {@link #METADATA_KEY_DISPLAY_ICON_URI} should be used instead. * * @see Builder#putBitmap(String, Bitmap) * @see #getBitmap(String) */ - public static final String METADATA_KEY_DISPLAY_ICON - = "android.media.metadata.DISPLAY_ICON"; + public static final String METADATA_KEY_DISPLAY_ICON = "android.media.metadata.DISPLAY_ICON"; /** - * An icon or thumbnail that is suitable for display to the user. When - * displaying more information for media described by this metadata the + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about the Uri of icon or thumbnail that is suitable for display to the user. + * When displaying more information for media described by this metadata, the * display description should be preferred to other fields when present. - * This must be a Uri style String. * * @see Builder#putText(String, CharSequence) * @see Builder#putString(String, String) @@ -311,7 +328,8 @@ public final class MediaMetadata2 { = "android.media.metadata.DISPLAY_ICON_URI"; /** - * A String key for identifying the content. This value is specific to the + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about the media ID of the content. This value is specific to the * service providing the content. If used, this should be a persistent * unique key for the underlying content. It may be used with * {@link MediaController2#playFromMediaId(String, Bundle)} @@ -326,11 +344,10 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID"; /** - * A Uri formatted String representing the content. This value is specific to the - * service providing the content. It may be used with - * {@link MediaController2#playFromUri(Uri, Bundle)} - * to initiate playback when provided by a {@link MediaBrowser2} connected to - * the same app. + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about the Uri of the content. This value is specific to the service providing the + * content. It may be used with {@link MediaController2#playFromUri(Uri, Bundle)} + * to initiate playback when provided by a {@link MediaBrowser2} connected to the same app. * * @see Builder#putText(String, CharSequence) * @see Builder#putString(String, String) @@ -340,7 +357,8 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI"; /** - * The radio frequency in Float format if this metdata representing radio content. + * The metadata key for a {@link Float} typed value to retrieve the information about the + * radio frequency if this metadata represents radio content. * * @see Builder#putFloat(String, float) * @see #getFloat(String) @@ -349,7 +367,8 @@ public final class MediaMetadata2 { "android.media.metadata.RADIO_FREQUENCY"; /** - * The radio callsign in String format if this metdata representing radio content. + * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the + * information about the radio callsign if this metadata represents radio content. * * @see Builder#putText(String, CharSequence) * @see Builder#putString(String, String) @@ -360,7 +379,8 @@ public final class MediaMetadata2 { "android.media.metadata.RADIO_CALLSIGN"; /** - * The bluetooth folder type of the media specified in the section 6.10.2.2 of the Bluetooth + * The metadata key for a {@link Long} typed value to retrieve the information about the + * bluetooth folder type of the media specified in the section 6.10.2.2 of the Bluetooth * AVRCP 1.5. It should be one of the following: * <ul> * <li>{@link #BT_FOLDER_TYPE_MIXED}</li> @@ -421,9 +441,10 @@ public final class MediaMetadata2 { public static final long BT_FOLDER_TYPE_YEARS = 6; /** - * Whether the media is an advertisement. A value of 0 indicates it is not an advertisement. A - * value of 1 or non-zero indicates it is an advertisement. If not specified, this value is set - * to 0 by default. + * The metadata key for a {@link Long} typed value to retrieve the information about whether + * the media is an advertisement. A value of 0 indicates it is not an advertisement. + * A value of 1 or non-zero indicates it is an advertisement. + * If not specified, this value is set to 0 by default. * * @see Builder#putLong(String, long) * @see #getLong(String) @@ -431,7 +452,8 @@ public final class MediaMetadata2 { public static final String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT"; /** - * The download status of the media which will be used for later offline playback. It should be + * The metadata key for a {@link Long} typed value to retrieve the information about the + * download status of the media which will be used for later offline playback. It should be * one of the following: * * <ul> @@ -721,6 +743,7 @@ public final class MediaMetadata2 { * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li> * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li> * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li> + * <li>{@link #METADATA_KEY_RADIO_CALLSIGN}</li> * </ul> * * @param key The key for referencing this value diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java index e0047d61af77..0d472abe9f9c 100644 --- a/media/java/android/media/MediaPlayer2.java +++ b/media/java/android/media/MediaPlayer2.java @@ -1339,6 +1339,7 @@ public abstract class MediaPlayer2 extends MediaPlayerBase * this method, you will have to initialize it again by setting the * data source and calling prepare(). */ + @Override public abstract void reset(); /** diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java index 373984745793..24274f1356e3 100644 --- a/media/java/android/media/MediaPlayerBase.java +++ b/media/java/android/media/MediaPlayerBase.java @@ -114,6 +114,11 @@ public abstract class MediaPlayerBase implements AutoCloseable { public abstract void pause(); /** + * Resets the MediaPlayerBase to its uninitialized state. + */ + public abstract void reset(); + + /** * */ public abstract void skipToNext(); diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp index 98e9a42d944d..e70d5ea0d566 100644 --- a/native/android/asset_manager.cpp +++ b/native/android/asset_manager.cpp @@ -18,9 +18,11 @@ #include <utils/Log.h> #include <android/asset_manager_jni.h> +#include <android_runtime/android_util_AssetManager.h> #include <androidfw/Asset.h> #include <androidfw/AssetDir.h> #include <androidfw/AssetManager.h> +#include <androidfw/AssetManager2.h> #include <utils/threads.h> #include "jni.h" @@ -35,21 +37,20 @@ using namespace android; // ----- struct AAssetDir { - AssetDir* mAssetDir; + std::unique_ptr<AssetDir> mAssetDir; size_t mCurFileIndex; String8 mCachedFileName; - explicit AAssetDir(AssetDir* dir) : mAssetDir(dir), mCurFileIndex(0) { } - ~AAssetDir() { delete mAssetDir; } + explicit AAssetDir(std::unique_ptr<AssetDir> dir) : + mAssetDir(std::move(dir)), mCurFileIndex(0) { } }; // ----- struct AAsset { - Asset* mAsset; + std::unique_ptr<Asset> mAsset; - explicit AAsset(Asset* asset) : mAsset(asset) { } - ~AAsset() { delete mAsset; } + explicit AAsset(std::unique_ptr<Asset> asset) : mAsset(std::move(asset)) { } }; // -------------------- Public native C API -------------------- @@ -104,19 +105,18 @@ AAsset* AAssetManager_open(AAssetManager* amgr, const char* filename, int mode) return NULL; } - AssetManager* mgr = static_cast<AssetManager*>(amgr); - Asset* asset = mgr->open(filename, amMode); - if (asset == NULL) { - return NULL; + ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(amgr)); + std::unique_ptr<Asset> asset = locked_mgr->Open(filename, amMode); + if (asset == nullptr) { + return nullptr; } - - return new AAsset(asset); + return new AAsset(std::move(asset)); } AAssetDir* AAssetManager_openDir(AAssetManager* amgr, const char* dirName) { - AssetManager* mgr = static_cast<AssetManager*>(amgr); - return new AAssetDir(mgr->openDir(dirName)); + ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(amgr)); + return new AAssetDir(locked_mgr->OpenDir(dirName)); } /** diff --git a/native/android/configuration.cpp b/native/android/configuration.cpp index 77237ae97ff5..87fe9edb49c5 100644 --- a/native/android/configuration.cpp +++ b/native/android/configuration.cpp @@ -17,9 +17,10 @@ #define LOG_TAG "Configuration" #include <utils/Log.h> -#include <androidfw/AssetManager.h> +#include <androidfw/AssetManager2.h> #include <android_runtime/android_content_res_Configuration.h> +#include <android_runtime/android_util_AssetManager.h> using namespace android; @@ -34,7 +35,11 @@ void AConfiguration_delete(AConfiguration* config) { } void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am) { - ((AssetManager*)am)->getConfiguration(out); + ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(am)); + ResTable_config config = locked_mgr->GetConfiguration(); + + // AConfiguration is not a virtual subclass, so we can memcpy. + memcpy(out, &config, sizeof(config)); } void AConfiguration_copy(AConfiguration* dest, AConfiguration* src) { diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index c16601604967..1d7234990ec1 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Private DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Kies private DNS-modus"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Af"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunisties"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Gasheernaam van private DNS-verskaffer"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Voer gasheernaam van DNS-verskaffer in"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Wys opsies vir draadlose skermsertifisering"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Hierdie kenmerk is eksperimenteel en kan werkverrigting beïnvloed."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Geneutraliseer deur <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Omtrent <xliff:g id="TIME">%1$s</xliff:g> oor"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Ongeveer <xliff:g id="TIME">%1$s</xliff:g> oor gegrond op jou gebruik"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> oor tot vol gelaai"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> oor"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Minder as <xliff:g id="THRESHOLD">%1$s</xliff:g> oor"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – minder as <xliff:g id="THRESHOLD">%2$s</xliff:g> oor"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>meer as <xliff:g id="TIME_REMAINING">%2$s</xliff:g> oor"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"meer as <xliff:g id="TIME_REMAINING">%1$s</xliff:g> oor"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"foon kan binnekort afgaan"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tablet kan binnekort afgaan"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"toestel kan binnekort afgaan"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – omtrent <xliff:g id="TIME">%2$s</xliff:g> oor"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – ongeveer <xliff:g id="TIME">%2$s</xliff:g> oor gegrond op jou gebruik"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – foon kan binnekort afgaan"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – tablet kan binnekort afgaan"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – toestel kan binnekort afgaan"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> oor tot vol gelaai"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot vol gelaai"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Onbekend"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Laai"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 6c8c69a30f47..a8b48aa3d51c 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"የግል ዲኤንኤስ"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"የግል ዲኤንኤስ ሁነታ ይምረጡ"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"ጠፍቷል"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"አድርባይ"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"የግል ዲኤንኤስ አቅራቢ አስተናጋጅ ስም"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"የዲኤንኤስ አቅራቢ አስተናጋጅ ስም ያስገቡ"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"የገመድ አልባ ማሳያ እውቅና ማረጋገጫ አማራጮችን አሳይ"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ይህ ባህሪ የሙከራ ነውና አፈጻጸም ላይ ተጽዕኖ ሊኖረው ይችላል።"</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"በ<xliff:g id="TITLE">%1$s</xliff:g> ተሽሯል"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"<xliff:g id="TIME">%1$s</xliff:g> አካባቢ ቀርቷል"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"በእርስዎ አጠቃቀም ላይ በመመስረት <xliff:g id="TIME">%1$s</xliff:g> ገደማ ቀርቷል"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"ሙሉ ኃይል እስኪሞላ ድረስ <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> ቀርቷል"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"ከ<xliff:g id="THRESHOLD">%1$s</xliff:g> ያነሰ ይቀራል"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - ከ<xliff:g id="THRESHOLD">%2$s</xliff:g> በታች ይቀራል"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>ከ<xliff:g id="TIME_REMAINING">%2$s</xliff:g> የበለጠ ይቀራል"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"ከ<xliff:g id="TIME_REMAINING">%1$s</xliff:g> በላይ ይቀራል"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"ስልኩ በቅርቡ ሊዘጋ ይችላል"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"ጡባዊ በቅርቡ ሊዘጋ ይችል ይሆናል"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"መሣሪያ በቅርቡ ሊዘጋ ይችል ይሆናል"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> አካባቢ ይቀራል"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - በእርስዎ አጠቃቀም ላይ በመመስረት <xliff:g id="TIME">%2$s</xliff:g> ገደማ ቀርቷል"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - ስልክ በቅርቡ ሊዘጋ ይችል ይሆናል"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - ጡባዊ በቅርቡ ሊዘጋ ይችል ይሆናል"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - መሣሪያ በቅርቡ ሊዘጋ ይችል ይሆናል"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"ኃይል በመሙላት ላይ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 40c16b9f8aa4..7f4d64366cf7 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"نظام أسماء النطاقات الخاص"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"اختر وضع نظام أسماء النطاقات الخاص"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"غير مفعّل"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"انتهازي"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"اسم مضيف مزوّد نظام أسماء النطاقات الخاص"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"أدخل اسم مضيف مزوّد نظام أسماء النطاقات"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"عرض خيارات شهادة عرض شاشة لاسلكي"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"هذه الميزة تجريبية وقد تؤثر في الأداء."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"تم الاستبدال بـ <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"يتبقى حوالي <xliff:g id="TIME">%1$s</xliff:g> لإتمام شحن البطارية"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"يتبقى <xliff:g id="TIME">%1$s</xliff:g> تقريبًا بناءً على استخدامك"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"يتبقى <xliff:g id="TIME">%1$s</xliff:g> لشحن البطارية بالكامل"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"يتبقى <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"يتبقى أقل من <xliff:g id="THRESHOLD">%1$s</xliff:g>."</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - يتبقى أقل من <xliff:g id="THRESHOLD">%2$s</xliff:g>."</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>يتبقى أكثر من <xliff:g id="TIME_REMAINING">%2$s</xliff:g>."</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"يتبقى أكثر من <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"قد يتم إغلاق الهاتف بعد قليل."</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"قد يتم إغلاق الجهاز اللوحي بعد قليل."</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"قد يتم إغلاق الجهاز بعد قليل."</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - يتبقى <xliff:g id="TIME">%2$s</xliff:g> تقريبًا"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - يتبقى <xliff:g id="TIME">%2$s</xliff:g> تقريبًا بناءً على استخدامك"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - قد يتم إغلاق الهاتف بعد قليل."</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - قد يتم إغلاق الجهاز اللوحي بعد قليل."</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - قد يتم إغلاق الجهاز بعد قليل."</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"جارٍ الشحن"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index b4d1af887447..c9dd0dab7a33 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -233,7 +233,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"অফ"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"সুবিধাবাদী"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"ব্যক্তিগত ডিএনএছ প্ৰদানকাৰীৰ হোষ্টনাম"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"ডিএনএছ সেৱা যোগানকাৰীৰ হ\'ষ্টনাম দিয়ক"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"বেতাঁৰ ডিছপ্লে প্ৰমাণপত্ৰৰ বাবে বিকল্পসমূহ দেখুৱাওক"</string> @@ -372,32 +373,42 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"এই সুবিধাটো পৰীক্ষামূলক, সেয়ে ই কাৰ্যক্ষমতাৰ ওপৰত প্ৰভাৱ পেলাব পাৰে।"</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g>ৰ দ্বাৰা অগ্ৰাহ্য কৰা হৈছে"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"প্ৰায় <xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"আপোনাৰ ব্যৱহাৰৰ ওপৰত ভিত্তি কৰি প্ৰায় <xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"সম্পূৰ্ণকৈ চ্চাৰ্জ হ\'বলৈ <xliff:g id="TIME">%1$s</xliff:g> বাকী"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> বাকী"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <!-- no translation found for power_remaining_less_than_duration_only (5996752448813295329) --> <skip /> - <!-- no translation found for power_remaining_less_than_duration (7967078125657859046) --> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> <skip /> - <!-- no translation found for power_remaining_more_than_subtext (6846716609975752316) --> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> <skip /> - <!-- no translation found for power_remaining_only_more_than_subtext (8884488700395194194) --> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> <skip /> - <!-- no translation found for power_remaining_duration_only_shutdown_imminent (8168317165722752881) --> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> <skip /> - <!-- no translation found for power_remaining_duration_only_shutdown_imminent (5957064378548718872) --> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> <skip /> - <!-- no translation found for power_remaining_duration_only_shutdown_imminent (9055596817716471373) --> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> <skip /> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - প্ৰায় <xliff:g id="TIME">%2$s</xliff:g> বাকী আছে"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - আপোনাৰ ব্যৱহাৰক ভিত্তি কৰি প্ৰায় <xliff:g id="TIME">%2$s</xliff:g> বাকী আছে"</string> - <!-- no translation found for power_remaining_duration_shutdown_imminent (7679005631124015335) --> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> <skip /> - <!-- no translation found for power_remaining_duration_shutdown_imminent (261050880878965621) --> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> <skip /> - <!-- no translation found for power_remaining_duration_shutdown_imminent (2020049829798578618) --> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> <skip /> <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_charging" msgid="1705179948350365604">"চাৰ্জ কৰি থকা হৈছে"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 97e35706d84e..2d235dd0b9b4 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Şəxsi DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Şəxsi DNS Rejimini Seçin"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Deaktiv"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Geniş imkanlar"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Şəxsi DNS provayderinin host adı"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS provayderinin host adını daxil edin"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Simsiz displey sertifikatlaşması üçün seçimləri göstərir"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Bu funksiya eksperimentaldır və performansa təsir edə bilər."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> tərəfindən qəbul edilmir"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Təxminən <xliff:g id="TIME">%1$s</xliff:g> qalıb"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"İstifadəyə əsasən təxminən <xliff:g id="TIME">%1$s</xliff:g> qalıb"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Tam enerji yığmağına <xliff:g id="TIME">%1$s</xliff:g> qalıb"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> qalıb"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Qalan vaxt <xliff:g id="THRESHOLD">%1$s</xliff:g> və daha azdır"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="THRESHOLD">%2$s</xliff:g> və daha az vaxt qalır"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> - Qalan vaxt <xliff:g id="TIME_REMAINING">%2$s</xliff:g> və daha azdır"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"Qalan vaxt <xliff:g id="TIME_REMAINING">%1$s</xliff:g> və daha çoxdur"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"telefon tezliklə sönə bilər"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"planşet tezliklə sönə bilər"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"cihaz tezliklə sönə bilər"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - təxminən <xliff:g id="TIME">%2$s</xliff:g> qalıb"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - istifadəyə əsasən təxminən <xliff:g id="TIME">%2$s</xliff:g> qalıb"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - telefon tezliklə sönə bilər"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - planşet tezliklə sönə bilər"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - cihaz tezliklə sönə bilər"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"Tam enerji yığmağına <xliff:g id="TIME">%1$s</xliff:g> qalıb"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tam enerji yığana kimi"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Naməlum"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Enerji doldurma"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index dd7043dc661b..1ea793127918 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Privatni DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Izaberite režim privatnog DNS-a"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Isključeno"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistički"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Ime hosta dobavljača usluge privatnog DNS-a"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Unesite ime hosta dobavljača usluge DNS-a"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Prikaz opcija za sertifikaciju bežičnog ekrana"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ova funkcija je eksperimentalna i može da utiče na kvalitet rada."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Zamenjuje ga <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Još oko <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Na osnovu potrošnje imate još otprilike <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> do potpunog punjenja"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Preostalo vreme: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – preostalo je manje od <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> – preostalo je više od <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"telefon će se uskoro isključiti"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tablet će se uskoro isključiti"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"uređaj će se uskoro isključiti"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – ostalo je oko <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – na osnovu potrošnje imate još otprilike <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – telefon će se uskoro isključiti"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – tablet će se uskoro isključiti"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – uređaj će se uskoro isključiti"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> do potpunog punjenja"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do potpunog punjenja"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Nepoznato"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Punjenje"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 1a0a251c9a3a..23a785c3d686 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"Выключана"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Гібкі"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Імя вузла аператара прыватнай DNS"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Увядзіце імя вузла аператара DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Паказаць опцыі сертыфікацыі бесправаднога дысплея"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Гэта функцыя з\'яўляецца эксперыментальнай і можа паўплываць на прадукцыйнасць."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Перавызначаны <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Засталося каля <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Засталося каля <xliff:g id="TIME">%1$s</xliff:g> на аснове вашага выкарыстання"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Да поўнай зарадкі засталося <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Засталося <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Засталося менш за <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – засталося менш за <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> – засталося больш за <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"засталося больш за <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"тэлефон у хуткім часе выключыцца"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"планшэт у хуткім часе выключыцца"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"прылада ў хуткім часе выключыцца"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – засталося каля <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – засталося каля <xliff:g id="TIME">%2$s</xliff:g> на аснове вашага выкарыстання"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – тэлефон у хуткім часе выключыцца"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – планшэт у хуткім часе выключыцца"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – прылада ў хуткім часе выключыцца"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"Зарадка"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index d071c64eea10..5cd345a2d06f 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"Изкл."</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"При възможност"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Име на хоста на доставчика на частния DNS"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Въведете името на хоста на DNS доставчика"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Показване на опциите за сертифициране на безжичния дисплей"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Тази функция е експериментална и може да се отрази на ефективността."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Заменено от „<xliff:g id="TITLE">%1$s</xliff:g>“"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Оставащо време: около <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Още около <xliff:g id="TIME">%1$s</xliff:g> въз основа на използването"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Оставащо време до пълно зареждане: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Оставащо време: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Остава/т по-малко от <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – остава/т по-малко от <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> – остава/т повече от <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"остава/т повече от <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"възможно е телефонът да се изключи скоро"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"възможно е таблетът да се изключи скоро"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"възможно е устройството да се изключи скоро"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – около <xliff:g id="TIME">%2$s</xliff:g> оставащо време"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – още около <xliff:g id="TIME">%2$s</xliff:g> въз основа на използването"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – възможно е телефонът да се изключи скоро"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – възможно е таблетът да се изключи скоро"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – възможно е устройството да се изключи скоро"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"Зарежда се"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index baa31e62bb32..1e56cf644e98 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"ব্যক্তিগত ডিএনএস"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"ব্যক্তিগত ডিএনএস মোড বেছে নিন"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"বন্ধ আছে"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"সুবিধাবাদী"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"ব্যক্তিগত ডিএনএস প্রদানকারীর হোস্টনেম"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"ডিএনএস প্রদানকারীর হোস্টনেম লিখুন"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ওয়্যারলেস প্রদর্শন সার্টিফিকেশন জন্য বিকল্পগুলি দেখান"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"এই বৈশিষ্ট্যটি পরীক্ষামূলক এবং এটি কার্য-সম্পাদনা প্রভাবিত করতে পারে।"</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> এর দ্বারা ওভাররাইড করা হয়েছে"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"প্রায় <xliff:g id="TIME">%1$s</xliff:g> বাকি"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"বর্তমান ব্যাটারি ব্যবহার অনুযায়ী আর <xliff:g id="TIME">%1$s</xliff:g> বাকি"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"সম্পূর্ণ চার্জ হতে <xliff:g id="TIME">%1$s</xliff:g> বাকি"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> এর থেকেও কম বাকি আছে"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="THRESHOLD">%2$s</xliff:g> এর থেকেও কম বাকি আছে"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g><xliff:g id="TIME_REMAINING">%2$s</xliff:g> এর থেকে বেশি বাকি আছে"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> এর থেকে বেশি বাকি আছে"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"ফোন শীঘ্রই বন্ধ হয়ে যেতে পারে"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"ট্যাবলেট শীঘ্রই বন্ধ হয়ে যেতে পারে"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"ডিভাইসটি শীঘ্রই বন্ধ হয়ে যেতে পারে"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - প্রায় <xliff:g id="TIME">%2$s</xliff:g> বাকি আছে"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - বর্তমান ব্যাটারি ব্যবহার অনুযায়ী আর <xliff:g id="TIME">%2$s</xliff:g> বাকি"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - ফোন শীঘ্রই বন্ধ হয়ে যেতে পারে"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - ট্যাবলেট শীঘ্রই বন্ধ হয়ে যেতে পারে"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - ডিভাইসটি শীঘ্রই বন্ধ হয়ে যেতে পারে"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"চার্জ হচ্ছে"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index c54a5dc69e5a..3c09c9df7631 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Privatni DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Odaberite način rada privatnog DNS-a"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Isključeno"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunistički"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Naziv host računara privatnog DNS-a"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Unesite naziv host računara pružaoca DNS-a"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Pokaži opcije za certifikaciju Bežičnog prikaza"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ova funkcija je eksperimentalna i može uticati na performanse."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Zamjenjuje <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Preostalo je otprilike još <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Preostalo je još oko <xliff:g id="TIME">%1$s</xliff:g>, na osnovu vašeg korištenja"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Još <xliff:g id="TIME">%1$s</xliff:g> do potpune napunjenosti"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Imate još <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – Preostalo je manje od <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>preostalo je više od <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"telefon će se uskoro isključiti"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tablet će se uskoro isključiti"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"uređaj će se uskoro isključiti"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - imate još <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - preostalo je još oko <xliff:g id="TIME">%2$s</xliff:g>, na osnovu vašeg korištenja"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – telefon će se uskoro isključiti"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – tablet će se uskoro isključiti"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – uređaj će se uskoro isključiti"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"Još <xliff:g id="TIME">%1$s</xliff:g> do potpune napunjenosti"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> do potpune napunjenosti"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Nepoznato"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Punjenje"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index fc312cf27c48..545dc10e7770 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privat"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selecciona el mode de DNS privat"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Desactivat"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunista"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nom d\'amfitrió del proveïdor de DNS privat"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Introdueix el nom d\'amfitrió del proveïdor de DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostra les opcions de certificació de pantalla sense fil"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Aquesta funció és experimental i pot afectar el rendiment."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"S\'ha substituït per <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Temps restant aproximat: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Temps restant aproximat segons l\'ús que en fas: <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> per completar la càrrega"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Temps restant: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Queda menys d\'un <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g>: queda menys d\'un <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>: el temps restant és superior a <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"el temps restant és superior a <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"és possible que el telèfon s\'apagui aviat"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"és possible que la tauleta s\'apagui aviat"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"és possible que el dispositiu s\'apagui aviat"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> aproximadament per esgotar la bateria"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g>; temps restant aproximat segons l\'ús que en fas: <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g>: és possible que el telèfon s\'apagui aviat"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g>: és possible que la tauleta s\'apagui aviat"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g>: és possible que el dispositiu s\'apagui aviat"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> per completar la càrrega"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconegut"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"S\'està carregant"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index e6000c1945aa..98455ddd3f63 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Soukromé DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Vyberte soukromý režim DNS"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Vypnuto"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Příležitostné"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Název hostitele poskytovatele soukromého DNS"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Zadejte název hostitele poskytovatele DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Zobrazit možnosti certifikace bezdrátového displeje"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Funkce je experimentální a může mít vliv na výkon."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Přepsáno nastavením <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Zbývá asi <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Při vašem obvyklém využití zbývá asi <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Plně se nabije za <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Zbývající čas: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Zbývá méně než <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – zbývá méně než <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> – zbývá více než <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"zbývá více než <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"telefon se brzy vypne"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tablet se brzy vypne"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"zařízení se brzy vypne"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – zbývá přibližně <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – při vašem obvyklém využití zbývá asi <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – telefon se brzy vypne"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – tablet se brzy vypne"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – zařízení se brzy vypne"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"Plně se nabije za <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – plně se nabije za <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Neznámé"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Nabíjí se"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index aa3e6b4983f2..291d4abc4006 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Privat DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Vælg privat DNS-tilstand"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Fra"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistisk"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Hostname for privat DNS-udbyder"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Angiv hostname for DNS-udbyder"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Vis valgmuligheder for certificering af trådløs skærm"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Denne funktion er eksperimentel og kan påvirke ydeevnen."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Tilsidesat af <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Ca. <xliff:g id="TIME">%1$s</xliff:g> tilbage"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Der er ca. <xliff:g id="TIME">%1$s</xliff:g> tilbage, alt efter hvordan du bruger enheden"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> til det er fuldt opladet"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> tilbage"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Der er mindre end <xliff:g id="THRESHOLD">%1$s</xliff:g> tilbage"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – der er mindre end <xliff:g id="THRESHOLD">%2$s</xliff:g> tilbage"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> – der er mere end <xliff:g id="TIME_REMAINING">%2$s</xliff:g> tilbage"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"der er mere end <xliff:g id="TIME_REMAINING">%1$s</xliff:g> tilbage"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"telefonen lukker muligvis snart ned"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"denne tablet lukker muligvis snart ned"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"enheden lukker muligvis snart ned"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – ca. <xliff:g id="TIME">%2$s</xliff:g> tilbage"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – der er ca. <xliff:g id="TIME">%2$s</xliff:g> tilbage, alt efter hvordan du bruger enheden"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – telefonen lukker muligvis snart ned"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – denne tablet lukker muligvis snart ned"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – enheden lukker muligvis snart ned"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> til det er fuldt opladet"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til det er fuldt opladet"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Ukendt"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Oplader"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 5210419a6e9b..84e33bc51ce3 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -56,14 +56,14 @@ <string name="bluetooth_connected" msgid="5427152882755735944">"Mit <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> verbunden"</string> <string name="bluetooth_pairing" msgid="1426882272690346242">"Verbindung wird hergestellt…"</string> <string name="bluetooth_connected_no_headset" msgid="616068069034994802">"Mit <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> verbunden (kein Telefon-Audio)"</string> - <string name="bluetooth_connected_no_a2dp" msgid="3736431800395923868">"Mit <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> verbunden (kein Media-Audio)"</string> + <string name="bluetooth_connected_no_a2dp" msgid="3736431800395923868">"Mit <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> verbunden (kein Medien-Audio)"</string> <string name="bluetooth_connected_no_map" msgid="3200033913678466453">"Mit <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> verbunden (kein Nachrichtenzugriff)"</string> - <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2047403011284187056">"Mit <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> verbunden (weder Telefon- noch Media-Audio)"</string> + <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2047403011284187056">"Mit <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> verbunden (weder Telefon- noch Medien-Audio)"</string> <string name="bluetooth_connected_battery_level" msgid="5162924691231307748">"Mit <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> verbunden, Akkustand bei <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> <string name="bluetooth_connected_no_headset_battery_level" msgid="1610296229139400266">"Mit <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> verbunden (kein Telefon-Audio), Akkustand bei <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> - <string name="bluetooth_connected_no_a2dp_battery_level" msgid="3908466636369853652">"Mit <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> verbunden (kein Media-Audio), Akkustand bei <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> - <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="1163440823807659316">"Mit <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> verbunden (weder Telefon- noch Media-Audio), Akkustand bei <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> - <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Media-Audio"</string> + <string name="bluetooth_connected_no_a2dp_battery_level" msgid="3908466636369853652">"Mit <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> verbunden (kein Medien-Audio), Akkustand bei <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> + <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="1163440823807659316">"Mit <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> verbunden (weder Telefon- noch Medien-Audio), Akkustand bei <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string> + <string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Medien-Audio"</string> <string name="bluetooth_profile_headset" msgid="7815495680863246034">"Telefonanrufe"</string> <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Dateiübertragung"</string> <string name="bluetooth_profile_hid" msgid="3680729023366986480">"Eingabegerät"</string> @@ -77,8 +77,8 @@ <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD-Audio"</string> <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Hörhilfe"</string> <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"Mit Hörhilfe verbunden"</string> - <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Verbunden mit Audiosystem von Medien"</string> - <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Verbunden mit Audiosystem des Telefons"</string> + <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"Verbunden mit Medien-Audio"</string> + <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"Verbunden mit Telefon-Audio"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"Mit Dateiübertragungsserver verbunden"</string> <string name="bluetooth_map_profile_summary_connected" msgid="8191407438851351713">"Mit Karte verbunden"</string> <string name="bluetooth_sap_profile_summary_connected" msgid="8561765057453083838">"Mit SAP verbunden"</string> @@ -89,8 +89,8 @@ <string name="bluetooth_pan_profile_summary_use_for" msgid="5736111170225304239">"Für Internetzugriff verwenden"</string> <string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Für Karte verwenden"</string> <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Für SIM-Zugriff verwenden"</string> - <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Für Audiosystem von Medien verwenden"</string> - <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Für Audiosystem des Telefons verwenden"</string> + <string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Für Medien-Audio verwenden"</string> + <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Für Telefon-Audio verwenden"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Für Dateiübertragung verwenden"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Für Eingabe verwenden"</string> <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Für Hörhilfe verwenden"</string> @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Privates DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Privaten DNS-Modus auswählen"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Aus"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistisch"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Hostname des privaten DNS-Anbieters"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Hostname des DNS-Anbieters eingeben"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Optionen zur Zertifizierung für kabellose Übertragung anzeigen"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Hierbei handelt es sich um eine experimentelle Funktion, die sich auf die Leistung auswirken kann."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Außer Kraft gesetzt von \"<xliff:g id="TITLE">%1$s</xliff:g>\""</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Ca. <xliff:g id="TIME">%1$s</xliff:g> übrig"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Noch ca. <xliff:g id="TIME">%1$s</xliff:g>, basierend auf deiner Nutzung"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> bis zur vollständigen Aufladung"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Noch <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Weniger als <xliff:g id="THRESHOLD">%1$s</xliff:g> verbleibend"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – weniger als <xliff:g id="THRESHOLD">%2$s</xliff:g> verbleibend"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> – mehr als <xliff:g id="TIME_REMAINING">%2$s</xliff:g> verbleibend"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"mehr als <xliff:g id="TIME_REMAINING">%1$s</xliff:g> verbleibend"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"Smartphone wird bald ausgeschaltet"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"Tablet wird bald ausgeschaltet"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"Gerät wird bald ausgeschaltet"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – ungefähr noch <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – noch ca. <xliff:g id="TIME">%2$s</xliff:g>, basierend auf deiner Nutzung"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – Smartphone wird bald ausgeschaltet"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – Tablet wird bald ausgeschaltet"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – Gerät wird bald ausgeschaltet"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> bis zur vollständigen Aufladung"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> bis vollständig geladen"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Unbekannt"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Wird aufgeladen"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index fe1c6bc9361e..55b01712b60d 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"Ανενεργή"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Καιροσκοπική"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Όνομα κεντρικού υπολογιστή παρόχου DNS"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Εισαγάγετε το όνομα κεντρικού υπολογιστή του παρόχου DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Εμφάνιση επιλογών για πιστοποίηση ασύρματης οθόνης"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Αυτή η λειτουργία είναι πειραματική και ενδεχομένως να επηρεάσει τις επιδόσεις."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Αντικαταστάθηκε από <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Απομένουν περίπου <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Απομένει/ουν περίπου <xliff:g id="TIME">%1$s</xliff:g> με βάση τη χρήση σας"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> έως την πλήρη φόρτιση"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Απομένει/ουν <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Απομένει/ουν λιγότερo/α από <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Απομένει λιγότερo από <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> - Απομένει περισσότερο από <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"απομένουν περισσότερα/ες από <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"το τηλέφωνο μπορεί να απενεργοποιηθεί σύντομα"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"το tablet μπορεί να απενεργοποιηθεί σύντομα"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"η συσκευή μπορεί να απενεργοποιηθεί σύντομα"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - απομένουν περίπου <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Απομένει/ουν περίπου <xliff:g id="TIME">%2$s</xliff:g> με βάση τη χρήση σας"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - το τηλέφωνο μπορεί να απενεργοποιηθεί σύντομα"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - το tablet μπορεί να απενεργοποιηθεί σύντομα"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - η συσκευή μπορεί να απενεργοποιηθεί σύντομα"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"Φόρτιση"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 5b8adcfb4817..8ff83b04f91b 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Private DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Select private DNS mode"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Off"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistic"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Private DNS provider hostname"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Enter hostname of DNS provider"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"This feature is experimental and may affect performance."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"About <xliff:g id="TIME">%1$s</xliff:g> left"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"About <xliff:g id="TIME">%1$s</xliff:g> left based on your usage"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> left until fully charged"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> left"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Less than <xliff:g id="THRESHOLD">%2$s</xliff:g> remaining"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>more than <xliff:g id="TIME_REMAINING">%2$s</xliff:g> remaining"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"more than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"phone may shutdown soon"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tablet may shutdown soon"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"device may shutdown soon"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – about <xliff:g id="TIME">%2$s</xliff:g> left"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - about <xliff:g id="TIME">%2$s</xliff:g> left based on your usage"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - phone may shutdown soon"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - tablet may shutdown soon"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - device may shutdown soon"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> left until fully charged"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> until fully charged"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index 5b8adcfb4817..8ff83b04f91b 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Private DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Select private DNS mode"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Off"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistic"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Private DNS provider hostname"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Enter hostname of DNS provider"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"This feature is experimental and may affect performance."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"About <xliff:g id="TIME">%1$s</xliff:g> left"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"About <xliff:g id="TIME">%1$s</xliff:g> left based on your usage"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> left until fully charged"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> left"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Less than <xliff:g id="THRESHOLD">%2$s</xliff:g> remaining"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>more than <xliff:g id="TIME_REMAINING">%2$s</xliff:g> remaining"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"more than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"phone may shutdown soon"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tablet may shutdown soon"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"device may shutdown soon"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – about <xliff:g id="TIME">%2$s</xliff:g> left"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - about <xliff:g id="TIME">%2$s</xliff:g> left based on your usage"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - phone may shutdown soon"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - tablet may shutdown soon"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - device may shutdown soon"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> left until fully charged"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> until fully charged"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 5b8adcfb4817..8ff83b04f91b 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Private DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Select private DNS mode"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Off"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistic"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Private DNS provider hostname"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Enter hostname of DNS provider"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"This feature is experimental and may affect performance."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"About <xliff:g id="TIME">%1$s</xliff:g> left"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"About <xliff:g id="TIME">%1$s</xliff:g> left based on your usage"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> left until fully charged"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> left"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Less than <xliff:g id="THRESHOLD">%2$s</xliff:g> remaining"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>more than <xliff:g id="TIME_REMAINING">%2$s</xliff:g> remaining"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"more than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"phone may shutdown soon"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tablet may shutdown soon"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"device may shutdown soon"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – about <xliff:g id="TIME">%2$s</xliff:g> left"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - about <xliff:g id="TIME">%2$s</xliff:g> left based on your usage"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - phone may shutdown soon"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - tablet may shutdown soon"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - device may shutdown soon"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> left until fully charged"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> until fully charged"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 5b8adcfb4817..8ff83b04f91b 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Private DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Select private DNS mode"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Off"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistic"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Private DNS provider hostname"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Enter hostname of DNS provider"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"This feature is experimental and may affect performance."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"About <xliff:g id="TIME">%1$s</xliff:g> left"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"About <xliff:g id="TIME">%1$s</xliff:g> left based on your usage"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> left until fully charged"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> left"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Less than <xliff:g id="THRESHOLD">%2$s</xliff:g> remaining"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>more than <xliff:g id="TIME_REMAINING">%2$s</xliff:g> remaining"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"more than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"phone may shutdown soon"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tablet may shutdown soon"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"device may shutdown soon"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – about <xliff:g id="TIME">%2$s</xliff:g> left"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - about <xliff:g id="TIME">%2$s</xliff:g> left based on your usage"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - phone may shutdown soon"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - tablet may shutdown soon"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - device may shutdown soon"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> left until fully charged"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> until fully charged"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index b7ca84a2d111..56e97e4cccad 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Private DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Select Private DNS Mode"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Off"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistic"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Private DNS provider hostname"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Enter hostname of DNS provider"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"This feature is experimental and may affect performance."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"About <xliff:g id="TIME">%1$s</xliff:g> left"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"About <xliff:g id="TIME">%1$s</xliff:g> left based on your usage"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> left until fully charged"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> left"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Less than <xliff:g id="THRESHOLD">%1$s</xliff:g> remaining"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Less than <xliff:g id="THRESHOLD">%2$s</xliff:g> remaining"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>more than <xliff:g id="TIME_REMAINING">%2$s</xliff:g> remaining"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"more than <xliff:g id="TIME_REMAINING">%1$s</xliff:g> remaining"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"phone may shutdown soon"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tablet may shutdown soon"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"device may shutdown soon"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - about <xliff:g id="TIME">%2$s</xliff:g> left"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - about <xliff:g id="TIME">%2$s</xliff:g> left based on your usage"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - phone may shutdown soon"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - tablet may shutdown soon"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - device may shutdown soon"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> left until fully charged"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until fully charged"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index bf62e598721b..bdff9a0228c8 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privado"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selecciona el modo de DNS privado"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Desactivado"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunista"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nombre de host del proveedor de DNS privado"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Ingresa el nombre de host del proveedor de DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opciones de certificación de pantalla inalámbrica"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta función es experimental y puede afectar el rendimiento."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Reemplazado por <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Tiempo restante aproximado: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Aproximadamente <xliff:g id="TIME">%1$s</xliff:g> restantes en función del uso"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> para completar la carga"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Tiempo restante: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> (tiempo restante: menos de <xliff:g id="THRESHOLD">%2$s</xliff:g>)"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> (tiempo restante: más de <xliff:g id="TIME_REMAINING">%2$s</xliff:g>)"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"Tiempo restante: más de <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"es posible que pronto se apague el teléfono"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"es posible que pronto se apague la tablet"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"es posible que pronto se apague el dispositivo"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> (tiempo restante: <xliff:g id="TIME">%2$s</xliff:g>)"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> (aproximadamente <xliff:g id="TIME">%2$s</xliff:g> restantes en función del uso)"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g>: es posible que pronto se apague el teléfono"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g>: es posible que pronto se apague la tablet"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g>: es posible que pronto se apague el dispositivo"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> para completar la carga"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> para completar la carga)"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconocido"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 5a9b87380142..940c84f9dad4 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privado"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selecciona el modo de DNS privado"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"No"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunista"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nombre de host de proveedor de DNS privado"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Introduce el nombre de host del proveedor de DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opciones para la certificación de la pantalla inalámbrica"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta función es experimental y puede afectar al rendimiento."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Tiempo restante aproximado: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Tiempo restante aproximado según tu uso: <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Tiempo restante hasta carga completa: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Tiempo restante: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tiempo restante: menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> (tiempo restante: menos de <xliff:g id="THRESHOLD">%2$s</xliff:g>)"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> (tiempo restante: más de <xliff:g id="TIME_REMAINING">%2$s</xliff:g>)"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"Tiempo restante: más de <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"es posible que el teléfono se apague pronto"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"es posible que el tablet se apague pronto"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"es posible que el dispositivo se apague pronto"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - tiempo aproximado restante: <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> (tiempo restante aproximado según tu uso: <xliff:g id="TIME">%2$s</xliff:g>)"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g>: es posible que el teléfono se apague pronto"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g>: es posible que el tablet se apague pronto"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g>: es posible que el dispositivo se apague pronto"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"Tiempo restante hasta carga completa: <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> para completar la carga"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconocido"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 77d55ffc5b05..a53b894a3325 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Privaatne DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Valige privaatse DNS-i režiim"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Väljas"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunistlik"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Privaatse DNS-i teenusepakkuja hostinimi"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Sisestage DNS-i teenusepakkuja hostinimi"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Juhtmeta ekraaniühenduse sertifitseerimisvalikute kuvamine"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"See funktsioon on katseline ja võib mõjutada toimivust."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Alistas <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Umbes <xliff:g id="TIME">%1$s</xliff:g> on jäänud"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Teie kasutuse alusel on jäänud ligikaudu <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> täislaadimiseni"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> on jäänud"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Jäänud on alla <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – jäänud on alla <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>jäänud on üle <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"jäänud on üle <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"telefon võib peagi välja lülituda"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tahvelarvuti võib peagi välja lülituda"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"seade võib peagi välja lülituda"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – umbes <xliff:g id="TIME">%2$s</xliff:g> on jäänud"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – teie kasutuse alusel on jäänud ligikaudu <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – telefon võib peagi välja lülituda"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – tahvelarvuti võib peagi välja lülituda"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – seade võib peagi välja lülituda"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> täislaadimiseni"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täislaadimiseni"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Tundmatu"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Laadimine"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 7da1e767be97..bcaf79cfbaf4 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -200,7 +200,7 @@ <string name="wifi_display_certification" msgid="8611569543791307533">"Hari gabeko bistaratze-egiaztatzea"</string> <string name="wifi_verbose_logging" msgid="4203729756047242344">"Gaitu Wi-Fi sareetan saioa hasteko modu xehatua"</string> <string name="wifi_connected_mac_randomization" msgid="3168165236877957767">"Konektatutako MAC helbideak ausaz aukeratzea"</string> - <string name="mobile_data_always_on" msgid="8774857027458200434">"Datu mugikorrak beti aktibo"</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> <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Desgaitu bolumen absolutua"</string> @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS pribatua"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Hautatu DNS pribatuaren modua"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Desaktibatuta"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Ahal bada"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"DNS hornitzaile pribatuaren ostalari-izena"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Idatzi DNS hornitzailearen ostalari-izena"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Erakutsi hari gabeko bistaratze-egiaztapenaren aukerak"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Eginbidea esperimentala da eta eragina izan dezake funtzionamenduan."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> hobespena gainjarri zaio"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"<xliff:g id="TIME">%1$s</xliff:g> inguru gelditzen dira"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"<xliff:g id="TIME">%1$s</xliff:g> inguru gelditzen dira, erabileraren arabera"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> falta dira guztiz kargatu arte"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> guztiz kargatu arte"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> baino gutxiago gelditzen dira"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="THRESHOLD">%2$s</xliff:g> baino gutxiago gelditzen dira"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME_REMAINING">%2$s</xliff:g> baino gehiago gelditzen dira"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> baino gehiago gelditzen dira"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"baliteke telefonoa laster itzaltzea"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"baliteke tableta laster itzaltzea"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"baliteke gailua laster itzaltzea"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> inguru gelditzen dira"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> inguru gelditzen dira, erabileraren arabera"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g>: baliteke telefonoa laster itzaltzea"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g>: baliteke tableta laster itzaltzea"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g>: baliteke gailua laster itzaltzea"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> falta dira guztiz kargatu arte"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Ezezaguna"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Kargatzen"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 2e5d699fb601..14e942dd9fb4 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"خاموش"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"فرصتطلب"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"نام میزبان ارائهدهنده DNS خصوصی"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"نام میزبان ارائهدهنده DNS خصوصی را وارد کنید"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"نمایش گزینهها برای گواهینامه نمایش بیسیم"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"این قابلیت آزمایشی است و ممکن است عملکرد را تحت تأثیر قرار دهد."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"توسط <xliff:g id="TITLE">%1$s</xliff:g> لغو شد"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"حدود <xliff:g id="TIME">%1$s</xliff:g> باقی مانده است"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"براساس میزان مصرف شما، <xliff:g id="TIME">%1$s</xliff:g> باقیمانده است"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> تا شارژ شدن کامل باقی مانده است"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> باقی مانده"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"کمتر از <xliff:g id="THRESHOLD">%1$s</xliff:g> باقی مانده"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - کمتر از <xliff:g id="THRESHOLD">%2$s</xliff:g> باقی مانده"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>بیشتر از <xliff:g id="TIME_REMAINING">%2$s</xliff:g> باقی مانده"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"بیشتر از <xliff:g id="TIME_REMAINING">%1$s</xliff:g> باقی مانده"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"ممکن است تلفن بهزودی خاموش شود"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"ممکن است رایانه لوحی بهزودی خاموش شود"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"ممکن است دستگاه بهزودی خاموش شود"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - تقریباً <xliff:g id="TIME">%2$s</xliff:g> باقی مانده است"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - براساس میزان مصرف شما، <xliff:g id="TIME">%2$s</xliff:g> باقیمانده است"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - ممکن است تلفن بهزودی خاموش شود"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - ممکن است رایانه لوحی بهزودی خاموش شود"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - ممکن است دستگاه بهزودی خاموش شود"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"در حال شارژ شدن"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 28d14a5fb344..040a47572395 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Yksityinen DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Valitse yksityinen DNS-tila"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Pois käytöstä"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistinen"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Yksityisen DNS-tarjoajan isäntänimi"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Anna isäntänimi tai DNS-tarjoaja."</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Näytä langattoman näytön sertifiointiin liittyvät asetukset"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Tämä ominaisuus on kokeellinen ja voi vaikuttaa suorituskykyyn."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Tämän ohittaa <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Noin <xliff:g id="TIME">%1$s</xliff:g> jäljellä"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Noin <xliff:g id="TIME">%1$s</xliff:g> jäljellä käytön perusteella"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> kunnes täynnä"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> jäljellä"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Alle <xliff:g id="THRESHOLD">%1$s</xliff:g> jäljellä"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – Alle <xliff:g id="THRESHOLD">%2$s</xliff:g> jäljellä"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> yli <xliff:g id="TIME_REMAINING">%2$s</xliff:g> jäljellä"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"yli <xliff:g id="TIME_REMAINING">%1$s</xliff:g> jäljellä"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"puhelin voi pian sammua"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tabletti voi pian sammua"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"laite voi pian sammua"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – noin <xliff:g id="TIME">%2$s</xliff:g> jäljellä"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – noin <xliff:g id="TIME">%2$s</xliff:g> jäljellä käytön perusteella"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – puhelin voi pian sammua"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – tabletti voi pian sammua"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – laite voi pian sammua"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> kunnes täynnä"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täyteen lataukseen"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Tuntematon"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Ladataan"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index ed6581db07bc..2e9caa61a8b7 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privé"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Sélectionnez le mode DNS privé"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Désactivé"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportuniste"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nom d\'hôte du fournisseur DNS privé"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Entrez le nom d\'hôte du fournisseur DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Afficher les options pour la certification d\'affichage sans fil"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Cette fonctionnalité est expérimentale et peut affecter les performances."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Il reste environ <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Il reste environ <xliff:g id="TIME">%1$s</xliff:g> en fonction de votre usage"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à la charge complète"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Temps restant : <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> : il reste moins de <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> : il reste plus de <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"il reste plus de <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"il se peut que le téléphone s\'éteigne bientôt"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"il se peut que la tablette s\'éteigne bientôt"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"il se peut que l\'appareil s\'éteigne bientôt"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> : il reste environ <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> : il reste environ <xliff:g id="TIME">%2$s</xliff:g> en fonction de votre usage"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> : il se peut que le téléphone s\'éteigne bientôt"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g>: il se peut que la tablette s\'éteigne bientôt"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> : il se peut que l\'appareil s\'éteigne bientôt"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> jusqu\'à la charge complète"</string> <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">"Charge en cours…"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 39d7aec06d36..8225c4d0318a 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privé"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Sélectionner le mode DNS privé"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Désactivé"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportuniste"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nom d\'hôte du fournisseur DNS privé"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Saisissez le nom d\'hôte du fournisseur DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Afficher les options de la certification de l\'affichage sans fil"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Cette fonctionnalité est expérimentale et peut affecter les performances."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Il reste environ <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Temps restant en fonction de votre utilisation : environ <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> avant charge complète"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Temps restant : <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – Il reste moins de <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> – Il reste plus de <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"Il reste plus de <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"Il est possible que le téléphone s\'éteigne bientôt"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"Il est possible que la tablette s\'éteigne bientôt"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"Il est possible que l\'appareil s\'éteigne bientôt"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - encore environ <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – Temps restant en fonction de votre utilisation : environ <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – Il est possible que le téléphone s\'éteigne bientôt"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – Il est possible que la tablette s\'éteigne bientôt"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – Il est possible que l\'appareil s\'éteigne bientôt"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> avant charge complète"</string> <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> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index effcd47f8279..839a25ea038f 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -93,7 +93,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Utilízase para o audio do teléfono"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Utilízase para a transferencia de ficheiros"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="232727040453645139">"Utilízase para a entrada"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Usar para o audiófono"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="908775281788309484">"Usar para os audiófonos"</string> <string name="bluetooth_pairing_accept" msgid="6163520056536604875">"Sincronizar"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="6061699265220789149">"SINCRONIZAR"</string> <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"Cancelar"</string> @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privado"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selecciona o modo de DNS privado"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Desactivar"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunista"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nome de host de provedor de DNS privado"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Introduce o nome de host de provedor de DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostra opcións para o certificado de visualización sen fíos"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta función é experimental e pode afectar ao rendemento."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Tempo que queda aproximadamente: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Tempo restante aproximado en función do uso: <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Tempo que queda ata cargar de todo: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Tempo restante: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tempo restante inferior a <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g>: tempo restante inferior a <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>: tempo restante superior a <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"tempo restante superior a: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"é posible que o teléfono se apague en breve"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"é posible que a tableta se apague en breve"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"é posible que o dispositivo se apague en breve"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> (tempo restante aproximado: <xliff:g id="TIME">%2$s</xliff:g>)"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tempo restante aproximado en función do uso: <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g>: é posible que o teléfono se apague en breve"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g>: é posible que a tableta se apague en breve"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g>: é posible que o dispositivo se apague en breve"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"Tempo que queda ata cargar de todo: <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> ata completar a carga"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Descoñecido"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index c517b8b741b9..9b54fa6248b0 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"બંધ"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"તકવાદી"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"ખાનગી DNS પ્રદાતા હોસ્ટનું નામ"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS પ્રદાતાના હોસ્ટનું નામ દાખલ કરો"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"વાયરલેસ ડિસ્પ્લે પ્રમાણપત્ર માટેના વિકલ્પો બતાવો"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"આ સુવિધા પ્રાયોગિક છે અને કામગીરી પર અસર કરી શકે છે."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> દ્વારા ઓવરરાઇડ થયું"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"અંદાજે <xliff:g id="TIME">%1$s</xliff:g> બાકી"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"તમારા વપરાશનાં આધારે લગભગ <xliff:g id="TIME">%1$s</xliff:g> બાકી છે"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"સંપૂર્ણપણે ચાર્જ થવામાં <xliff:g id="TIME">%1$s</xliff:g> બાકી"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> બાકી"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> કરતાં ઓછો સમય બાકી છે"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="THRESHOLD">%2$s</xliff:g> કરતાં ઓછો સમય બાકી છે"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g><xliff:g id="TIME_REMAINING">%2$s</xliff:g> કરતાં વધુ સમય બાકી છે"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> કરતાં વધુ સમય બાકી છે"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"ફોન થોડી વારમાં બંધ થઈ શકે છે"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"ટૅબ્લેટ થોડી વારમાં બંધ થઈ શકે છે"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"ઉપકરણ થોડી વારમાં બંધ થઈ શકે છે"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - લગભગ <xliff:g id="TIME">%2$s</xliff:g> બાકી"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - તમારા વપરાશનાં આધારે લગભગ <xliff:g id="TIME">%2$s</xliff:g> બાકી છે"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - ફોન થોડી વારમાં બંધ થઈ શકે છે"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - ટૅબ્લેટ થોડી વારમાં બંધ થઈ શકે છે"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - ઉપકરણ થોડી વારમાં બંધ થઈ શકે છે"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"ચાર્જ થઈ રહ્યું છે"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 2726c4677a3a..9179f2e86a07 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"बंद"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"मौका मिलते ही काम करने वाला"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"निजी DNS सेवा देने वाले का होस्टनाम"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS सेवा देने वाले का होस्टनाम डालें"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"वायरलेस दिखाई देने के लिए प्रमाणन विकल्प दिखाएं"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"यह सुविधा प्रायोगिक है और निष्पादन को प्रभावित कर सकती है."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> के द्वारा ओवरराइड किया गया"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"लगभग <xliff:g id="TIME">%1$s</xliff:g> शेष"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"आपके उपयोग के आधार पर लगभग <xliff:g id="TIME">%1$s</xliff:g> का समय बचा है"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"पूरी तरह से चार्ज होने में <xliff:g id="TIME">%1$s</xliff:g> शेष"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> शेष"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> से कम समय बचा है"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="THRESHOLD">%2$s</xliff:g> से कम बैटरी बची है"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> <xliff:g id="TIME_REMAINING">%2$s</xliff:g> से ज़्यादा बैटरी बची है"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> से ज़्यादा बैटरी बची है"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"फ़ोन जल्दी ही बंद हो सकता है"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"टैबलेट जल्दी ही बंद हो सकता है"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"डिवाइस जल्दी ही बंद हो सकता है"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - लगभग <xliff:g id="TIME">%2$s</xliff:g> शेष"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - आपके उपयोग के आधार पर लगभग <xliff:g id="TIME">%2$s</xliff:g> का समय बचा है"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - फ़ोन जल्दी ही बंद हो सकता है"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - टैबलेट जल्दी ही बंद हो सकता है"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - डिवाइस जल्दी ही बंद हो सकता है"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"चार्ज हो रही है"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index f7d59f0d7f59..f0a7fc446141 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Privatni DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Odaberite način privatnog DNS-a"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Isključeno"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunistički"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Naziv hosta davatelja usluge privatnog DNS-a"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Unesite naziv hosta davatelja usluge DNS-a"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Prikaži opcije za certifikaciju bežičnog prikaza"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ova je značajka eksperimentalna i može utjecati na performanse."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Premošćeno postavkom <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Još otprilike <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Još otprilike <xliff:g id="TIME">%1$s</xliff:g> na temelju vaše upotrebe"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Još <xliff:g id="TIME">%1$s</xliff:g> do potpune napunjenosti"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Još <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo je manje od <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – preostalo je manje od <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> – preostalo je više od <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"preostalo je više od <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"telefon bi se uskoro mogao isključiti"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tablet bi se uskoro mogao isključiti"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"uređaj bi se uskoro mogao isključiti"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – preostalo je približno <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – još otprilike <xliff:g id="TIME">%2$s</xliff:g> na temelju vaše upotrebe"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – telefon bi se uskoro mogao isključiti"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – tablet bi se uskoro mogao isključiti"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – uređaj bi se uskoro mogao isključiti"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"Još <xliff:g id="TIME">%1$s</xliff:g> do potpune napunjenosti"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do potpune napunjenosti"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Nepoznato"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Punjenje"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 35a556098f01..b354ea96f3d3 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Privát DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"„Privát DNS” mód kiválasztása"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Ki"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunista"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Privát DNS-szolgáltató gazdagépneve"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Adja meg a DNS-szolgáltató gazdagépnevét"</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> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ez egy kísérleti funkció, és hatással lehet a teljesítményre."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Felülírva erre: <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Körülbelül <xliff:g id="TIME">%1$s</xliff:g> maradt hátra"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Körülbelül <xliff:g id="TIME">%1$s</xliff:g> van hátra az eszköz igénybevétele alapján"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> a teljes töltöttségig"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> van hátra"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Kevesebb mint <xliff:g id="THRESHOLD">%1$s</xliff:g> van hátra"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – kevesebb mint <xliff:g id="THRESHOLD">%2$s</xliff:g> van hátra"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> – több mint <xliff:g id="TIME_REMAINING">%2$s</xliff:g> van hátra"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"több mint <xliff:g id="TIME_REMAINING">%1$s</xliff:g> van hátra"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"előfordulhat, hogy a telefon hamarosan leáll"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"előfordulhat, hogy a táblagép hamarosan leáll"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"előfordulhat, hogy az eszköz hamarosan leáll"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - körülbelül <xliff:g id="TIME">%2$s</xliff:g> van hátra"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – körülbelül <xliff:g id="TIME">%2$s</xliff:g> van hátra az eszköz igénybevétele alapján"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – előfordulhat, hogy a telefon hamarosan leáll"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – előfordulhat, hogy a táblagép hamarosan leáll"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – előfordulhat, hogy az eszköz hamarosan leáll"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> a teljes töltöttségig"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> a teljes feltöltésig"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Ismeretlen"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Töltés"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 44a17149d289..8e52ceb74a9c 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"Անջատված է"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Ճկուն"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Անհատական DNS ծառայության մատակարարի խնամորդի անունը"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Մուտքագրեք DNS ծառայության մատակարարի խնամորդի անունը"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Ցույց տալ անլար էկրանի հավաստագրման ընտրանքները"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Սա փորձնական գործառույթ է և կարող է ազդել սարքի աշխատանքի վրա:"</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Գերազանցված է <xliff:g id="TITLE">%1$s</xliff:g>-ից"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Մնացել է մոտ <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Մնացել է մոտ <xliff:g id="TIME">%1$s</xliff:g>՝ օգտագործման եղանակից կախված"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Մինչև լրիվ լիցքավորումը մնացել է <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Մնացել է <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Մնացել է <xliff:g id="THRESHOLD">%1$s</xliff:g>-ից պակաս"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g>՝ մնացել է <xliff:g id="THRESHOLD">%2$s</xliff:g>-ից պակաս"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>՝ մնացել է ավելի քան <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"մնացել է ավելի քան <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"հնարավոր է հեռախոսը շուտով կանջատվի"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"հնարավոր է պլանշետը շուտով կանջատվի"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"հնարավոր է սարքը շուտով կանջատվի"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – մնացել է մոտ <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - մնացել է մոտ <xliff:g id="TIME">%2$s</xliff:g>՝ օգտագործման եղանակից կախված"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g>՝ հնարավոր է հեռախոսը շուտով կանջատվի"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g>՝ հնարավոր է պլանշետը շուտով կանջատվի"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g>՝ հնարավոր է սարքը շուտով կանջատվի"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"Լիցքավորում"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index c02bd90426a0..3e8c6766de8d 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS Pribadi"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Pilih Mode DNS Pribadi"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Nonaktif"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunistik"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Hostname penyedia DNS pribadi"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Masukkan hostname penyedia DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Tampilkan opsi untuk sertifikasi layar nirkabel"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Fitur ini bersifat eksperimental dan dapat memengaruhi kinerja."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Digantikan oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Sekitar <xliff:g id="TIME">%1$s</xliff:g> lagi"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Kira-kira <xliff:g id="TIME">%1$s</xliff:g> lagi berdasarkan penggunaan Anda"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> lagi hingga terisi penuh"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> tersisa"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tersisa kurang dari <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tersisa kurang dari <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>tersisa lebih dari <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"tersisa lebih dari <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"ponsel mungkin segera dimatikan"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tablet mungkin segera dimatikan"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"perangkat mungkin segera dimatikan"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - kira-kira <xliff:g id="TIME">%2$s</xliff:g> lagi"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - kira-kira <xliff:g id="TIME">%2$s</xliff:g> lagi berdasarkan penggunaan Anda"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - ponsel mungkin segera dimatikan"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - tablet mungkin segera dimatikan"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - perangkat mungkin segera dimatikan"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> lagi hingga terisi penuh"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi terisi penuh"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Tidak diketahui"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Mengisi daya"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 36baf6c89ddf..0e90dd69baf4 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Lokað DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Velja lokaða DNS-stillingu"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Slökkt"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Eftir hentugleika"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Hýsilheiti lokaðrar DNS-veitu"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Slá inn hýsilheiti DNS-veitu"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Sýna valkosti fyrir vottun þráðlausra skjáa"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Þessi eiginleiki er á tilraunastigi og getur haft áhrif á frammistöðu."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Hnekkt af <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Um það bil <xliff:g id="TIME">%1$s</xliff:g> eftir"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"U.þ.b. <xliff:g id="TIME">%1$s</xliff:g> eftir miðað við notkun þína"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> þar til hleðslu er lokið"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> eftir"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Minna en <xliff:g id="THRESHOLD">%1$s</xliff:g> eftir"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – Minna en <xliff:g id="THRESHOLD">%2$s</xliff:g> eftir"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>meira en <xliff:g id="TIME_REMAINING">%2$s</xliff:g> eftir"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"meira en <xliff:g id="TIME_REMAINING">%1$s</xliff:g> eftir"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"síminn gæti slökkt á sér fljótlega"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"spjaldtölvan gæti slökkt á sér fljótlega"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"tækið gæti slökkt á sér fljótlega"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – um <xliff:g id="TIME">%2$s</xliff:g> eftir"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – u.þ.b. <xliff:g id="TIME">%2$s</xliff:g> eftir miðað við notkun þína"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – síminn gæti slökkt á sér fljótlega"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – spjaldtölvan gæti slökkt á sér fljótlega"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – tækið gæti slökkt á sér fljótlega"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> þar til hleðslu er lokið"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> þar til fullri hleðslu er náð"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Óþekkt"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Í hleðslu"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index f19c14c2a76a..0ac2b8e0c4eb 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privato"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Seleziona modalità DNS privato"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Non attiva"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistica"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nome host del provider DNS privato"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Inserisci il nome host del provider DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostra opzioni per la certificazione display wireless"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Questa funzione è sperimentale e potrebbe influire sulle prestazioni."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Valore sostituito da <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Tempo approssimativo rimanente: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Tempo rimanente in base al tuo utilizzo: <xliff:g id="TIME">%1$s</xliff:g> circa"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Tempo rimanente alla carica completa: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Tempo rimanente: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tempo rimanente: meno di <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tempo rimanente: meno di <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tempo rimanente: più di <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"Tempo rimanente: più di <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"Il telefono potrebbe spegnersi a breve"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"Il tablet potrebbe spegnersi a breve"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"Il dispositivo potrebbe spegnersi a breve"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - ancora circa <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tempo rimanente in base al tuo utilizzo: <xliff:g id="TIME">%2$s</xliff:g> circa"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g>: il telefono potrebbe spegnersi a breve"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g>: il tablet potrebbe spegnersi a breve"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g>: il dispositivo potrebbe spegnersi a breve"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"Tempo rimanente alla carica completa: <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> alla carica completa"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Sconosciuta"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"In carica"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 81307f729289..7c4eb7cd4e89 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"מושבת"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"אופרטוניסטי"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"שם מארח של ספק DNS פרטי"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"צריך להזין את שם המארח של ספק DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"הצג אפשרויות עבור אישור של תצוגת WiFi"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"תכונה זו היא ניסיונית ועשויה להשפיע על הביצועים."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"נעקף על ידי <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"עוד <xliff:g id="TIME">%1$s</xliff:g> בקירוב"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"על סמך השימוש במכשיר, הסוללה תתרוקן בעוד <xliff:g id="TIME">%1$s</xliff:g>, בקירוב"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> עד לטעינה מלאה"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"נותרו <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"נותרו פחות מ-<xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - הזמן שנותר, פחות מ-<xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>הזמן שנותר: יותר מ-<xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"הזמן שנותר: יותר מ-<xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"ייתכן שהטלפון ייכבה בקרוב"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"ייתכן שהטאבלט ייכבה בקרוב"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"ייתכן שהמכשיר ייכבה בקרוב"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - הזמן הנותר: בערך <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - על סמך השימוש במכשיר, הסוללה תתרוקן בעוד <xliff:g id="TIME">%2$s</xliff:g>, בקירוב"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - ייתכן שהטלפון ייכבה בקרוב"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - ייתכן שהטאבלט ייכבה בקרוב"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - ייתכן שהמכשיר ייכבה בקרוב"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"טוען"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 346a96d35bc0..c5836d64f236 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"OFF"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"日和見"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"プライベート DNS プロバイダのホスト名"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS プロバイダのホスト名を入力"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ワイヤレスディスプレイ認証のオプションを表示"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"この機能は試験運用機能であり、パフォーマンスに影響することがあります。"</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g>によって上書き済み"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"あと約 <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"残り時間: 約 <xliff:g id="TIME">%1$s</xliff:g>(使用状況に基づく)"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"フル充電まであと <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g>(残り時間)"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"残り時間: <xliff:g id="THRESHOLD">%1$s</xliff:g>未満"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - 残り時間: <xliff:g id="THRESHOLD">%2$s</xliff:g>未満"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> ー 残り時間: <xliff:g id="TIME_REMAINING">%2$s</xliff:g>以上"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"残り時間: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>以上"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"スマートフォンの電源がもうすぐ切れます"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"タブレットの電源がもうすぐ切れます"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"端末の電源がもうすぐ切れます"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - 残り約 <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - 残り時間: 約 <xliff:g id="TIME">%2$s</xliff:g>(使用状況に基づく)"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - スマートフォンの電源がもうすぐ切れます"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - タブレットの電源がもうすぐ切れます"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - 端末の電源がもうすぐ切れます"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"充電中"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index db03069070ae..caf15ccafe6a 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"გამორთული"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"ოპორტუნისტული"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"პირადი DNS პროვაიდერის სერვერის სახელი"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"შეიყვანეთ DNS პროვაიდერის სერვერის სახელი"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"უსადენო ეკრანის სერტიფიცირების ვარიანტების ჩვენება"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ეს ფუნქცია საცდელია და შეიძლება გავლენა იქონიოს ფუნქციონალობაზე."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"უკუგებულია <xliff:g id="TITLE">%1$s</xliff:g>-ის მიერ"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"დარჩა დაახლოებით <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"დარჩა დაახლოებით <xliff:g id="TIME">%1$s</xliff:g>, ბატარეის მოხმარების გათვალისწინებით"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"სრულ დატენვამდე დარჩენილია <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"დარჩენილია <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"დარჩენილია <xliff:g id="THRESHOLD">%1$s</xliff:g>-ზე ნაკლები"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> — დარჩენილია <xliff:g id="THRESHOLD">%2$s</xliff:g>-ზე ნაკლები"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>დარჩენილია <xliff:g id="TIME_REMAINING">%2$s</xliff:g>-ზე მეტი"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"დარჩენილია <xliff:g id="TIME_REMAINING">%1$s</xliff:g>-ზე მეტი"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"ტელეფონი შეიძლება მალე გაითიშოს"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"ტაბლეტი შეიძლება მალე გაითიშოს"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"მოწყობილობა შეიძლება მალე გაითიშოს"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> — დარჩა დაახლოებით <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> — დარჩა დაახლოებით <xliff:g id="TIME">%2$s</xliff:g>, ბატარეის მოხმარების გათვალისწინებით"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> — ტელეფონი შეიძლება მალე გაითიშოს"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> — ტაბლეტი შეიძლება მალე გაითიშოს"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> — მოწყობილობა შეიძლება მალე გაითიშოს"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"იტენება"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index acd7d875c7ba..08733ae3f375 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"Өшіру"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Кездейсоқ"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Жеке DNS провайдерінің хост атауы"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS провайдерінің хост атауын енгізіңіз"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Сымсыз дисплей растау опцияларын көрсету"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Бұл мүмкіндік эксперименттік болып табылады және өнімділікке әсер етуі мүмкін."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> үстінен басқан"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Қалған <xliff:g id="TIME">%1$s</xliff:g> туралы"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Пайдалану негізінде шамамен <xliff:g id="TIME">%1$s</xliff:g> қалды"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Толық зарядқа <xliff:g id="TIME">%1$s</xliff:g> қалды"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> қалды"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> шамасынан аз қалды"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="THRESHOLD">%2$s</xliff:g> шамасынан аз қалды"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g><xliff:g id="TIME_REMAINING">%2$s</xliff:g> үстінде қалды"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> үстінде қалды"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"телефон көп ұзамай өшуі мүмкін"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"планшет көп ұзамай өшуі мүмкін"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"құрылғы көп ұзамай өшуі мүмкін"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – шамамен <xliff:g id="TIME">%2$s</xliff:g> қалды"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - пайдалану негізінде шамамен <xliff:g id="TIME">%2$s</xliff:g> қалды"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – телефон көп ұзамай өшуі мүмкін"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – планшет көп ұзамай өшуі мүмкін"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – құрылғы көп ұзамай өшуі мүмкін"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"Зарядталуда"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 3da37587e828..57022a84d1e1 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"បិទ"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistic"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"ឈ្មោះម៉ាស៊ីនក្រុមហ៊ុនផ្ដល់សេវា DNS ឯកជន"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"បញ្ចូលឈ្មោះម៉ាស៊ីនរបស់ក្រុមហ៊ុនផ្ដល់សេវា DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"បង្ហាញជម្រើសសម្រាប់វិញ្ញាបនបត្របង្ហាញឥតខ្សែ"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"មុខងារនេះគឺជាការពិសោធន៍ ហើយអាចប៉ះពាល់ដំណើរការ។"</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"បដិសេធដោយ <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"សល់ប្រហែល <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"សល់ប្រហែល <xliff:g id="TIME">%1$s</xliff:g> ទៀតផ្អែកលើការប្រើប្រាស់របស់អ្នក"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"សល់ <xliff:g id="TIME">%1$s</xliff:g> ទើបសាកថ្មពេញ"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"នៅសល់ <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"នៅសល់តិចជាង <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g>៖ នៅសល់តិចជាង <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>៖ នៅសល់ច្រើនជាង <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"នៅសល់ច្រើនជាង <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"ទូរសព្ទអាចនឹងបិទក្នុងពេលបន្តិចទៀត"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"ថេប្លេតអាចនឹងបិទក្នុងពេលបន្តិចទៀត"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"ឧបករណ៍អាចនឹងបិទក្នុងពេលបន្តិចទៀត"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - សល់ប្រហែល <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - សល់ប្រហែល <xliff:g id="TIME">%2$s</xliff:g> ទៀតផ្អែកលើការប្រើប្រាស់របស់អ្នក"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g>៖ ទូរសព្ទអាចនឹងបិទក្នុងពេលបន្តិចទៀត"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g>៖ ថេប្លេតអាចនឹងបិទក្នុងពេលបន្តិចទៀត"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g>៖ ឧបករណ៍អាចនឹងបិទក្នុងពេលបន្តិចទៀត"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"កំពុងបញ្ចូលថ្ម"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 9f55983693b7..8a5b2515ae8e 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"ಆಫ್"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"ಅವಕಾಶವಾದಿ"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"ಖಾಸಗಿ DNS ಪೂರೈಕೆದಾರರ ಹೋಸ್ಟ್ಹೆಸರು"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS ಪೂರೈಕೆದಾರರ ಹೋಸ್ಟ್ಹೆಸರನ್ನು ನಮೂದಿಸಿ"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ವೈರ್ಲೆಸ್ ಪ್ರದರ್ಶನ ಪ್ರಮಾಣೀಕರಣಕ್ಕಾಗಿ ಆಯ್ಕೆಗಳನ್ನು ತೋರಿಸು"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ಇದು ಪ್ರಾಯೋಗಿಕ ವೈಶಿಷ್ಟ್ಯವಾಗಿದೆ. ಕಾರ್ಯಕ್ಷಮತೆ ಮೇಲೆ ಪರಿಣಾಮ ಬೀರಬಹುದು."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ಮೂಲಕ ಅತಿಕ್ರಮಿಸುತ್ತದೆ"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"ಸುಮಾರು <xliff:g id="TIME">%1$s</xliff:g> ಬಾಕಿಯಿದೆ"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"ನಿಮ್ಮ ಬಳಕೆಯ ಆಧಾರದ ಮೇಲೆ ಸುಮಾರು <xliff:g id="TIME">%1$s</xliff:g> ಉಳಿದಿದೆ"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"ಸಂಪೂರ್ಣ ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> ಉಳಿದಿದೆ"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ನಿಮಿಷಕ್ಕಿಂತ ಕಡಿಮೆ ಸಮಯ ಉಳಿದಿದೆ"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - ರಷ್ಟು <xliff:g id="THRESHOLD">%2$s</xliff:g> ಕ್ಕಿಂತ ಕಡಿಮೆ ಸಮಯ ಬಾಕಿ ಉಳಿದಿದೆ"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> ರಷ್ಟು <xliff:g id="TIME_REMAINING">%2$s</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು ಸಮಯ ಬಾಕಿ ಉಳಿದಿದೆ"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ಕ್ಕಿಂತ ಹೆಚ್ಚು ಸಮಯ ಉಳಿದಿದೆ"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"ಫೋನ್ ಶೀಘ್ರದಲ್ಲೇ ಶಟ್ ಡೌನ್ ಆಗಬಹುದು"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"ಟ್ಯಾಬ್ಲೆಟ್ ಶೀಘ್ರದಲ್ಲೇ ಶಟ್ ಡೌನ್ ಆಗಬಹುದು"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"ಸಾಧನವು ಶೀಘ್ರದಲ್ಲೇ ಶಟ್ ಡೌನ್ ಆಗಬಹುದು"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಸುಮಾರು <xliff:g id="TIME">%2$s</xliff:g> ಬಾಕಿಯಿದೆ"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - ನಿಮ್ಮ ಬಳಕೆಯ ಆಧಾರದ ಮೇಲೆ <xliff:g id="TIME">%2$s</xliff:g> ಉಳಿದಿದೆ"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಫೋನ್ ಶೀಘ್ರದಲ್ಲೇ ಶಟ್ ಡೌನ್ ಆಗಬಹುದು"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಟ್ಯಾಬ್ಲೆಟ್ ಶೀಘ್ರದಲ್ಲೇ ಶಟ್ ಡೌನ್ ಆಗಬಹುದು"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಸಾಧನವು ಶೀಘ್ರದಲ್ಲೇ ಶಟ್ ಡೌನ್ ಆಗಬಹುದು"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 35a2867fc7d5..af1859abe4b7 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"사용 안함"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"기회주의적"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"비공개 DNS 제공업체 호스트 이름"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS 제공업체의 호스트 이름 입력"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"무선 디스플레이 인증서 옵션 표시"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"실험실 기능이며 성능에 영향을 줄 수 있습니다."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> 우선 적용됨"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"약 <xliff:g id="TIME">%1$s</xliff:g> 남음"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"내 사용량을 기준으로 약 <xliff:g id="TIME">%1$s</xliff:g> 남음"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"충전 완료까지 <xliff:g id="TIME">%1$s</xliff:g> 남음"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> 남음"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> 미만 남음"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="THRESHOLD">%2$s</xliff:g> 미만 남음"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g><xliff:g id="TIME_REMAINING">%2$s</xliff:g> 이상 남음"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> 이상 남음"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"휴대전화가 곧 종료될 수 있음"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"태블릿이 곧 종료될 수 있음"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"기기가 곧 종료될 수 있음"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - 약 <xliff:g id="TIME">%2$s</xliff:g> 남음"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - 내 사용량을 기준으로 약 <xliff:g id="TIME">%2$s</xliff:g> 남음"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - 휴대전화가 곧 종료될 수 있음"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - 태블릿이 곧 종료될 수 있음"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - 기기가 곧 종료될 수 있음"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"충전 중"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index c1f73cc897d4..baf27532602d 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"Өчүк"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Ийкемдүү"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Купуя DNS түйүндүн аталышы"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS түйүндүн аталышын киргизиңиз"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Зымсыз дисплейди сертификатто мүмкүнчүлүктөрүн көргөзүү"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Бул сынамык мүмкүнчүлүк болгондуктан, түзмөктүн иштешине таасир этиши мүмкүн."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> менен алмаштырылган"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Батарея түгөнгөнгө чейин калган убакыт: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Колдонушуңузга караганда болжол менен <xliff:g id="TIME">%1$s</xliff:g> калды"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Батарея толгонго чейин калган убакыт: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> калды"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> жетпеген убакыт калды"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="THRESHOLD">%2$s</xliff:g> жетпеген убакыт калды"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME_REMAINING">%2$s</xliff:g> көп убакыт калды"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> көп убакыт калды"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"телефон бир аздан кийин өчүп калышы мүмкүн"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"планшет бир аздан кийин өчүп калышы мүмкүн"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"түзмөк бир аздан кийин өчүп калышы мүмкүн"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – болжол менен <xliff:g id="TIME">%2$s</xliff:g> калды"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - колдонушуңузга караганда болжол менен <xliff:g id="TIME">%2$s</xliff:g> калды"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – телефон бир аздан кийин өчүп калышы мүмкүн"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – планшет бир аздан кийин өчүп калышы мүмкүн"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – түзмөк бир аздан кийин өчүп калышы мүмкүн"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"Кубатталууда"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index a5c8d564d9cd..5f673f2b9d6e 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"ປິດ"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"ກ່ຽວກັບໂອກາດ"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"ຊື່ໂຮສຜູ້ໃຫ້ບໍລິການ DNS ສ່ວນຕົວ"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"ລະບຸຊື່ໂຮສຂອງຜູ້ໃຫ້ບໍລິການ DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ສະແດງໂຕເລືອກສຳລັບການສະແດງການຮັບຮອງລະບົບໄຮ້ສາຍ"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ຄຸນສົມບັດນີ້ກຳລັງຢູ່ໃນການທົດລອງແລະອາດມີຜົນຕໍ່ປະສິດທິພາບ."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"ຖືກແທນໂດຍ <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"ອີກປະມານ <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"ເຫຼືອອີກປະມານ <xliff:g id="TIME">%1$s</xliff:g> ໂດຍອ້າງອີງຈາກການນຳໃຊ້ຂອງທ່ານ"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> ຈົນກວ່າຈະສາກເຕັມ"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"ຍັງເຫຼືອ <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"ຍັງເຫຼືອໜ້ອຍກວ່າ <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - ຍັງເຫຼືອໜ້ອຍກວ່າ <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>ຍັງເຫຼືອຫຼາຍກວ່າ <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"ຍັງເຫຼືອຫຼາຍກວ່າ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"ໂທລະສັບອາດຈະປິດໃນໄວໆນີ້"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"ແທັຍເລັດອາດຈະປິດໃນໄວໆນີ້"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"ອຸປະກອນອາດຈະປິດໃນໄວໆນີ້"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - ຍັງເຫຼືອປະມານ <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - ເຫຼືອອີກປະມານ <xliff:g id="TIME">%2$s</xliff:g> ໂດຍອ້າງອີງຈາກການນຳໃຊ້ຂອງທ່ານ"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - ໂທລະສັບອາດຈະປິດໃນໄວໆນີ້"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - ແທັບເລັດອາດຈະປິດໃນໄວໆນີ້"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - ອຸປະກອນອາດຈະປິດໃນໄວໆນີ້"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"ກຳລັງສາກໄຟ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 8eced2de7f71..eb48b7f6a78b 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Privatus DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Pasirinkite privataus DNS režimą"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Išjungta"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunistinis"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Privataus DNS teikėjo prieglobos serverio pavadinimas"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Įveskite DNS teikėjo prieglobos serverio pavadinimą"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Rodyti belaidžio rodymo sertifikavimo parinktis"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ši funkcija yra eksperimentinė ir ji gali turėti įtakos našumui."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Nepaisyta naudojant nuostatą „<xliff:g id="TITLE">%1$s</xliff:g>“"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Liko maždaug <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Liko maždaug <xliff:g id="TIME">%1$s</xliff:g>, atsižvelgiant į naudojimą"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Iki visiškos įkrovos liko <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Liko <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Liko mažiau nei <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – liko mažiau nei <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> – liko daugiau nei <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"Liko daugiau nei <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"telefonas netrukus gali būti išjungtas"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"planšetinis komp. netrukus gali būti išjungtas"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"įrenginys netrukus gali būti išjungtas"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – liko maždaug <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – liko maždaug <xliff:g id="TIME">%2$s</xliff:g>, atsižvelgiant į naudojimą"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – telefonas netrukus gali būti išjungtas"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – planšetinis kompiuteris netrukus gali būti išjungtas"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – įrenginys netrukus gali būti išjungtas"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"Iki visiškos įkrovos liko <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> iki visiško įkrovimo"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Nežinomas"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Kraunasi..."</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 24cc69eef366..96a9d1e0b7cc 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Privāts DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Atlasiet privāta DNS režīmu"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Izslēgts"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportūnistisks"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Privātā DNS pakalpojumu sniedzēja saimniekdatora nosaukums"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Ievadiet DNS pakalpojumu sniedzēja saimniekdatora nosaukumu"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Rādīt bezvadu attēlošanas sertifikācijas iespējas"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Šī funkcija ir eksperimentāla un var ietekmēt veiktspēju."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Jaunā preference: <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Atlikušais laiks: aptuveni <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Atlikušais laiks: aptuveni <xliff:g id="TIME">%1$s</xliff:g> (ņemot vērā lietojumu)"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Atlikušais laiks līdz pilnai uzlādei: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Atlicis: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Atlikušais laiks — mazāk nekā <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> — atlikušais laiks ir mazāk nekā <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> — atlikušais laiks ir vairāk nekā <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"Atlikušais laiks — vairāk nekā <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"Tālrunis, iespējams, drīz izslēgsies."</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"Planšetdators, iespējams, drīz izslēgsies."</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"Ierīce, iespējams, drīz izslēgsies."</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - vēl apmēram <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> — atlikušais laiks: aptuveni <xliff:g id="TIME">%2$s</xliff:g> (ņemot vērā lietojumu)"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> — tālrunis, iespējams, drīz izslēgsies."</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> — planšetdators, iespējams, drīz izslēgsies."</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> — ierīce, iespējams, drīz izslēgsies."</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"Atlikušais laiks līdz pilnai uzlādei: <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>, kamēr pilnībā uzlādēts"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Nezināms"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Uzlāde"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 4798a6fc5151..534e994c4212 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"Исклучено"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Опортунистички"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Име на хост на оператор на приватен DNS"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Внесете име на хост на операторот на DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Покажи ги опциите за безжичен приказ на сертификат"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Функцијата е експериментална и може да влијае на изведбата."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Прескокнато според <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Преостануваат околу <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Уште околу <xliff:g id="TIME">%1$s</xliff:g> според користењето"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Преостануваат <xliff:g id="TIME">%1$s</xliff:g> дури се наполни целосно"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"уште <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Уште помалку од <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Уште помалку од <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>- Уште повеќе од <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"Уште повеќе од <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"телефонот може да се исклучи наскоро"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"таблетот може да се исклучи наскоро"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"уредот може да се исклучи наскоро"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - уште околу <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - уште околу <xliff:g id="TIME">%2$s</xliff:g> според користењето"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - телефонот може да се исклучи наскоро"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - таблетот може да се исклучи наскоро"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - уредот може да се исклучи наскоро"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"Се полни"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 04b08f4f2fc4..20a1e29f843b 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"ഓഫ്"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"അവസരവാദപരം"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"സ്വകാര്യ DNS ദാതാവിന്റെ ഹോസ്റ്റുനാമം"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS ദാതാവിന്റെ ഹോസ്റ്റുനാമം നൽകുക"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"വയർലെസ് ഡിസ്പ്ലേ സർട്ടിഫിക്കേഷനായി ഓപ്ഷനുകൾ ദൃശ്യമാക്കുക"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ഈ ഫീച്ചർ പരീക്ഷണാത്മകമായതിനാൽ പ്രകടനത്തെ ബാധിച്ചേക്കാം."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ഉപയോഗിച്ച് അസാധുവാക്കി"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"ഏകദേശം <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"നിങ്ങളുടെ ഉപയോഗത്തെ അടിസ്ഥാനമാക്കി ഏതാണ്ട് <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"മുഴുവൻ ചാർജാകാൻ <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-ൽ കുറവ് സമയം ശേഷിക്കുന്നു"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="THRESHOLD">%2$s</xliff:g>-ൽ കുറവ് സമയം ശേഷിക്കുന്നു"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g><xliff:g id="TIME_REMAINING">%2$s</xliff:g>-ൽ കൂടുതൽ സമയം ശേഷിക്കുന്നു"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>-ൽ കൂടുതൽ സമയം ശേഷിക്കുന്നു"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"ഫോൺ ഉടൻ ഷട്ട് ഡൗൺ ആയേക്കാം"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"ടാബ്ലെറ്റ് ഉടൻ ഷട്ട് ഡൗൺ ആയേക്കാം"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"ഉപകരണം ഉടൻ ഷട്ട്ഡൗൺ ആയേക്കാം"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - ഏതാണ്ട് <xliff:g id="TIME">%2$s</xliff:g> ശേഷിക്കുന്നു"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - നിങ്ങളുടെ ഉപയോഗത്തെ അടിസ്ഥാനമാക്കി ഏതാണ്ട് <xliff:g id="TIME">%2$s</xliff:g> ശേഷിക്കുന്നു"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - ഫോൺ ഉടൻ ഷട്ട് ഡൗൺ ആയേക്കാം"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - ടാബ്ലെറ്റ് ഉടൻ ഷട്ട് ഡൗൺ ആയേക്കാം"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - ഉപകരണം ഉടൻ ഷട്ട്ഡൗൺ ആയേക്കാം"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"ചാർജ്ജുചെയ്യുന്നു"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 596d1039797d..04ca84ac4c48 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"Унтраалттай"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Оппортунист үзэлтэн"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Хувийн DNS-н үйлчилгээ үзүүлэгчийн хостын нэр"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS-н үйлчилгээ үзүүлэгчийн хостын нэрийг оруулах"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Утасгүй дэлгэцийн сертификатын сонголтыг харуулах"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Энэ функц туршилтынх бөгөөд ажиллагаанд нөлөөлж болзошгүй."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Давхарласан <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Ойролцоогоор <xliff:g id="TIME">%1$s</xliff:g> үлдсэн"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Таны хэрэглээнд тулгуурлан <xliff:g id="TIME">%1$s</xliff:g> орчмын хугацаа үлдсэн байна"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Бүрэн цэнэглэх хүртэл <xliff:g id="TIME">%1$s</xliff:g> үлдсэн"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> үлдсэн"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>-с бага хугацаа үлдсэн"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="THRESHOLD">%2$s</xliff:g>-с бага хугацаа үлдсэн"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g><xliff:g id="TIME_REMAINING">%2$s</xliff:g>-с их хугацаа үлдсэн"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>-с их хугацаа үлдсэн"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"утас удахгүй унтарч болзошгүй"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"таблет удахгүй унтарч болзошгүй"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"төхөөрөмж удахгүй унтарч болзошгүй"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g>-с <xliff:g id="TIME">%2$s</xliff:g> үлдсэн"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - таны хэрэглээнд тулгуурлан <xliff:g id="TIME">%2$s</xliff:g> орчмын хугацаа үлдсэн байна"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - утас удахгүй унтарч болзошгүй"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - таблет удахгүй унтарч болзошгүй"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - төхөөрөмж удахгүй унтарч болзошгүй"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"Цэнэглэж байна"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 26f4344db6e7..de9328249f55 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"बंद"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"संधिसाधू"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"खाजगी DNS पुरवठादार होस्ट नाव"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS पुरवठादाराचे होस्टनाव टाका"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"वायरलेस डिस्प्ले प्रमाणिकरणाचे पर्याय दाखवा"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"हे वैशिष्ट्य प्रायोगिक आहे आणि कदाचित कार्यप्रदर्शन प्रभावित करू शकते."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारे अधिलिखित"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"सुमारे <xliff:g id="TIME">%1$s</xliff:g> शिल्लक"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"तुमच्या वापरानुसार अंदाजे <xliff:g id="TIME">%1$s</xliff:g> पुरेल इतकी बॅटरी शिल्लक आहे"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"पूर्णपणे चार्ज होण्यास <xliff:g id="TIME">%1$s</xliff:g> शिल्लक"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> शिल्लक"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> पेक्षा कमी शिल्लक आहे"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="THRESHOLD">%2$s</xliff:g> पेक्षा कमी शिल्लक आहे"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g><xliff:g id="TIME_REMAINING">%2$s</xliff:g> पेक्षा जास्त शिल्लक आहे"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> पेक्षा जास्त शिल्लक आहे"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"फोन लवकरच बंद होऊ शकतो"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"टॅबलेट लवकरच बंद होऊ शकतो"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"डिव्हाइस लवकरच बंद होऊ शकते"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - सुमारे <xliff:g id="TIME">%2$s</xliff:g> शिल्लक"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - तुमच्या वापरानुसार अंदाजे <xliff:g id="TIME">%2$s</xliff:g> पुरेल इतकी बॅटरी शिल्लक आहे"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - फोन लवकरच बंद होऊ शकतो"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - टॅबलेट लवकरच बंद होऊ शकतो"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - डिव्हाइस लवकरच बंद होऊ शकते"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"चार्ज होत आहे"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 8e24ceedcee8..99f6c0a85538 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS Peribadi"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Pilih Mod DNS Peribadi"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Mati"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunistik"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nama hos pembekal DNS peribadi"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Masukkan nama hos pembekal DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Tunjukkan pilihan untuk pensijilan paparan wayarles"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ciri ini adalah percubaan dan boleh menjejaskan prestasi."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Diatasi oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Kira-kira <xliff:g id="TIME">%1$s</xliff:g> lagi"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Tinggal kira-kira <xliff:g id="TIME">%1$s</xliff:g> berdasarkan penggunaan anda"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> lagi sehingga dicas penuh"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> lagi"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Tinggal kurang daripada <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tinggal kurang daripada <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>tinggal lebih daripada <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"tinggal lebih daripada <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"telefon mungkin ditutup tidak lama lagi"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tablet mungkin ditutup tidak lama lagi"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"peranti mungkin ditutup tidak lama lagi"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - kira-kira <xliff:g id="TIME">%2$s</xliff:g> lagi"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - tinggal kira-kira <xliff:g id="TIME">%2$s</xliff:g> berdasarkan penggunaan anda"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - telefon mungkin ditutup tidak lama lagi"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - tablet mungkin ditutup tidak lama lagi"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - peranti mungkin ditutup tidak lama lagi"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> lagi sehingga dicas penuh"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga dicas penuh"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Tidak diketahui"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Mengecas"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index c1ca0ab0331a..aedb2910fc27 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -45,7 +45,7 @@ <string name="available_via_carrier" msgid="1469036129740799053">"%1$s မှတစ်ဆင့် ရနိုင်သည်"</string> <string name="speed_label_very_slow" msgid="1867055264243608530">"အလွန်နှေး"</string> <string name="speed_label_slow" msgid="813109590815810235">"နှေး"</string> - <string name="speed_label_okay" msgid="2331665440671174858">"အိုကေ"</string> + <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string> <string name="speed_label_medium" msgid="3175763313268941953">"အတော်အသင့်"</string> <string name="speed_label_fast" msgid="7715732164050975057">"မြန်"</string> <string name="speed_label_very_fast" msgid="2265363430784523409">"အလွန်မြန်"</string> @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"ပိတ်ရန်"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"အခွင့်အရေးသမား"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"သီးသန့် DNS ပံ့ပိုးသူ၏ အင်တာနက်လက်ခံဝန်ဆောင်ပေးသူအမည်"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS ဝန်ဆောင်မှုပေးသူ၏ အင်တာနက်လက်ခံဝန်ဆောင်ပေးသူအမည်ကို ထည့်ပါ"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ကြိုးမဲ့ အခင်းအကျင်း အသိအမှတ်ပြုလက်မှတ်အတွက် ရွေးချယ်စရာများပြရန်"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ဤဝန်ဆောင်မှုမှာ စမ်းသပ်အဆင့်သာဖြစ်၍ လုပ်ဆောင်မှုအားနည်းနိုင်သည်။"</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> မှ ကျော်၍ လုပ်ထားသည်။"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"<xliff:g id="TIME">%1$s</xliff:g> ခန့်လိုပါသည်"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"သင့်အသုံးပြုမှုအရ <xliff:g id="TIME">%1$s</xliff:g> ခန့် ကျန်ပါသည်"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"အားပြည့်ရန် <xliff:g id="TIME">%1$s</xliff:g> လိုပါသည်"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> ကျန်သည်"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ခန့်သာ ကျန်တော့သည်"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="THRESHOLD">%2$s</xliff:g> ခန့်သာ ကျန်တော့သည်"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g><xliff:g id="TIME_REMAINING">%2$s</xliff:g> ကျော် ကျန်သေးသည်"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ကျော် ကျန်သေးသည်"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"မကြာမီ ဖုန်းပိတ်သွားနိုင်သည်"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"မကြာမီ တက်ဘလက်ပိတ်သွားနိုင်သည်"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"မကြာမီ စက်ပိတ်သွားနိုင်သည်"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> − <xliff:g id="TIME">%2$s</xliff:g> ခန့်ကျန်သည်"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - သင့်အသုံးပြုမှုအရ <xliff:g id="TIME">%2$s</xliff:g> ခန့် ကျန်ပါသည်"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - မကြာမီ ဖုန်းပိတ်သွားနိင်သည်"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - မကြာမီ တက်ဘလက်ပိတ်သွားနိင်သည်"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - မကြာမီ စက်ပိတ်သွားနိင်သည်"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"အားသွင်းနေပါသည်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 0b54624409fa..fadf15404d91 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Privat DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Velg Privat DNS-modus"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Av"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistisk"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Vertsnavn for privat DNS-leverandør"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Skriv inn vertsnavnet til DNS-leverandøren"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Vis alternativer for sertifisering av trådløs skjerm"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Dette er en eksperimentell funksjon som kan gjøre at telefonen ikke fungerer optimalt."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overstyres av <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Omtrent <xliff:g id="TIME">%1$s</xliff:g> gjenstår"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Omtrent <xliff:g id="TIME">%1$s</xliff:g> igjen basert på bruken din"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> til det er fulladet"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> gjenstår"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Mindre enn <xliff:g id="THRESHOLD">%1$s</xliff:g> gjenstår"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – mindre enn <xliff:g id="THRESHOLD">%2$s</xliff:g> gjenstår"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> mindre enn <xliff:g id="TIME_REMAINING">%2$s</xliff:g> gjenstår"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"mer enn <xliff:g id="TIME_REMAINING">%1$s</xliff:g> gjenstår"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"telefonen slås kanskje av snart"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"nettbrettet slås kanskje av snart"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"enheten slås kanskje av snart"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – omtrent <xliff:g id="TIME">%2$s</xliff:g> gjenstår"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – omtrent <xliff:g id="TIME">%2$s</xliff:g> igjen basert på bruken din"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – telefonen slås kanskje av snart"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – nettbrettet slås kanskje av snart"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – enheten slås kanskje av snart"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> til det er fulladet"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til det er fulladet"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Ukjent"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Lader"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 7b780096ed0b..e778c8eb05fb 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"निष्क्रिय छ"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"अवसरवादी"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"निजी DNS प्रदायकको होस्टनाम"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS प्रदायकको होस्टनाम प्रविष्ट गर्नुहोस्"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ताररहित प्रदर्शन प्रमाणीकरणका लागि विकल्पहरू देखाउनुहोस्"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"यो सुविधा प्रयोगात्मक छ र प्रदर्शनमा असर गर्न सक्छ।"</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारा अधिरोहित"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"लगभग <xliff:g id="TIME">%1$s</xliff:g> बाँकी"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"तपाईंको प्रयोगका आधारमा लगभग <xliff:g id="TIME">%1$s</xliff:g> बाँकी"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"पूर्णरूपमा चार्ज हुन <xliff:g id="TIME">%1$s</xliff:g> बाँकी"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"बाँकी समय <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> भन्दा कम समय बाँकी छ"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="THRESHOLD">%2$s</xliff:g> भन्दा कम समय बाँकी छ"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> <xliff:g id="TIME_REMAINING">%2$s</xliff:g> भन्दा बढी समय बाँकी छ"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> भन्दा बढी समय बाँकी छ"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"फोन चाँडै बन्द हुन सक्छ"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"ट्याब्लेट चाँडै बन्द हुन सक्छ"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"यन्त्र चाँडै बन्द हुन सक्छ"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - करिब <xliff:g id="TIME">%2$s</xliff:g> बाँकी"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - तपाईंको प्रयोगका आधारमा लगभग <xliff:g id="TIME">%2$s</xliff:g> बाँकी"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - फोन चाँडै बन्द हुन सक्छ"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - ट्याब्लेट चाँडै बन्द हुन सक्छ"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - यन्त्र चाँडै बन्द हुन सक्छ"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"चार्ज हुँदै"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index c6577e6bb6f5..dc56ac4b5aa6 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Privé-DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selecteer de modus Privé-DNS"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Uit"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistisch"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Hostnaam van privé-DNS-provider"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Geef hostnaam van DNS-provider op"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Opties weergeven voor certificering van draadloze weergave"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Deze functie is experimenteel en kan invloed hebben op de prestaties."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overschreven door <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Nog ongeveer <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Nog ongeveer <xliff:g id="TIME">%1$s</xliff:g> over op basis van je gebruik"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Nog <xliff:g id="TIME">%1$s</xliff:g> tot volledig opgeladen"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> resterend"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Minder dan <xliff:g id="THRESHOLD">%1$s</xliff:g> resterend"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Minder dan <xliff:g id="THRESHOLD">%2$s</xliff:g> resterend"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> - meer dan <xliff:g id="TIME_REMAINING">%2$s</xliff:g> resterend"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"meer dan <xliff:g id="TIME_REMAINING">%1$s</xliff:g> resterend"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"telefoon wordt binnenkort mogelijk uitgeschakeld"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tablet wordt binnenkort mogelijk uitgeschakeld"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"apparaat wordt binnenkort mogelijk uitgeschakeld"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - ongeveer <xliff:g id="TIME">%2$s</xliff:g> resterend"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g>: nog ongeveer <xliff:g id="TIME">%2$s</xliff:g> over op basis van je gebruik"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - telefoon wordt binnenkort mogelijk uitgeschakeld"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - tablet wordt binnenkort mogelijk uitgeschakeld"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - apparaat wordt binnenkort mogelijk uitgeschakeld"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"Nog <xliff:g id="TIME">%1$s</xliff:g> tot volledig opgeladen"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot volledig opgeladen"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Onbekend"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Opladen"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 0c2cbdac6ae0..ab7e9fa2b5a8 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -233,7 +233,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"ଅଫ୍"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"ସୁଯୋଗବାଦୀ"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"ବ୍ୟକ୍ତିଗତ DNS ପ୍ରଦାତା ହୋଷ୍ଟନାମ"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS ପ୍ରଦାନକାରୀଙ୍କ ହୋଷ୍ଟନାମ ପ୍ରବେଶ କରନ୍ତୁ"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ୱେୟାରଲେସ୍ ପ୍ରଦର୍ଶନ ସାର୍ଟିଫିକେସନ୍ ପାଇଁ ବିକଳ୍ପଗୁଡିକ ଦେଖାନ୍ତୁ"</string> @@ -372,32 +373,42 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ଏହି ପରୀକ୍ଷାମୂଳକ ବୈଶିଷ୍ଟ୍ୟ ପର୍ଫର୍ମେନ୍ସକୁ ପ୍ରଭାବିତ କରିପାରେ।"</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ଦ୍ୱାରା ଓଭର୍ରାଇଡ୍ କରାଯାଇଛି"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"ପ୍ରାୟ <xliff:g id="TIME">%1$s</xliff:g> ଅବଶିଷ୍ଟ ରହିଛି"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"ଆପଣଙ୍କ ବ୍ୟବହାରକୁ ଆଧାର କରି ପ୍ରାୟ <xliff:g id="TIME">%1$s</xliff:g> ଅବଶିଷ୍ଟ"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"ସମ୍ପୂର୍ଣ୍ଣ ଚାର୍ଜ ହେବାପାଇଁ <xliff:g id="TIME">%1$s</xliff:g> ଅବଶିଷ୍ଟ ଅଛି"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> ଅବଶିଷ୍ଟ"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <!-- no translation found for power_remaining_less_than_duration_only (5996752448813295329) --> <skip /> - <!-- no translation found for power_remaining_less_than_duration (7967078125657859046) --> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> <skip /> - <!-- no translation found for power_remaining_more_than_subtext (6846716609975752316) --> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> <skip /> - <!-- no translation found for power_remaining_only_more_than_subtext (8884488700395194194) --> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> <skip /> - <!-- no translation found for power_remaining_duration_only_shutdown_imminent (8168317165722752881) --> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> <skip /> - <!-- no translation found for power_remaining_duration_only_shutdown_imminent (5957064378548718872) --> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> <skip /> - <!-- no translation found for power_remaining_duration_only_shutdown_imminent (9055596817716471373) --> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> <skip /> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - ପ୍ରାୟ <xliff:g id="TIME">%2$s</xliff:g> ଅବଶିଷ୍ଟ ରହିଛି"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - ଆପଣଙ୍କ ବ୍ୟବହାରକୁ ଆଧାର କରି ପ୍ରାୟ <xliff:g id="TIME">%2$s</xliff:g> ଅବଶିଷ୍ଟ"</string> - <!-- no translation found for power_remaining_duration_shutdown_imminent (7679005631124015335) --> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> <skip /> - <!-- no translation found for power_remaining_duration_shutdown_imminent (261050880878965621) --> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> <skip /> - <!-- no translation found for power_remaining_duration_shutdown_imminent (2020049829798578618) --> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> <skip /> <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_charging" msgid="1705179948350365604">"ଚାର୍ଜ ହେଉଛି"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 6549861a11dc..2287d232a61e 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"ਬੰਦ"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"ਮੌਕਾਪ੍ਰਸਤ"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"ਨਿੱਜੀ DNS ਪ੍ਰਦਾਨਕ ਹੋਸਟਨਾਮ"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS ਪ੍ਰਦਾਨਕ ਦਾ ਹੋਸਟਨਾਮ ਦਾਖਲ ਕਰੋ"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ਵਾਇਰਲੈੱਸ ਡਿਸਪਲੇ ਪ੍ਰਮਾਣੀਕਰਨ ਲਈ ਚੋਣਾਂ ਪ੍ਰਦਰਸ਼ਿਤ ਕਰੋ"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ਇਹ ਵਿਸ਼ੇਸ਼ਤਾ ਪ੍ਰਯੋਗਾਤਮਿਕ ਹੈ ਅਤੇ ਪ੍ਰਦਰਸ਼ਨ ਤੇ ਅਸਰ ਪਾ ਸਕਦੀ ਹੈ।"</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ਦੁਆਰਾ ਓਵਰਰਾਈਡ ਕੀਤਾ"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"ਲਗਭਗ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"ਤੁਹਾਡੀ ਵਰਤੋਂ ਦੇ ਆਧਾਰ \'ਤੇ ਲਗਭਗ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"ਪੂਰੀ ਤਰ੍ਹਾਂ ਚਾਰਜ ਹੋਣ ਲਈ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> ਤੋਂ ਘੱਟ ਸਮਾਂ ਬਾਕੀ"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="THRESHOLD">%2$s</xliff:g> ਤੋਂ ਘੱਟ ਸਮਾਂ ਬਾਕੀ"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g><xliff:g id="TIME_REMAINING">%2$s</xliff:g> ਤੋਂ ਵੱਧ ਸਮਾਂ ਬਾਕੀ"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> ਤੋਂ ਵੱਧ ਸਮਾਂ ਬਾਕੀ"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"ਫ਼ੋਨ ਛੇਤੀ ਹੀ ਬੰਦ ਹੋ ਸਕਦਾ ਹੈ"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"ਟੈਬਲੈੱਟ ਛੇਤੀ ਹੀ ਬੰਦ ਹੋ ਸਕਦਾ ਹੈ"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"ਡੀਵਾਈਸ ਛੇਤੀ ਹੀ ਬੰਦ ਹੋ ਸਕਦਾ ਹੈ"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਲਗਭਗ <xliff:g id="TIME">%2$s</xliff:g> ਬਾਕੀ"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਤੁਹਾਡੀ ਵਰਤੋਂ ਦੇ ਆਧਾਰ \'ਤੇ ਲਗਭਗ <xliff:g id="TIME">%2$s</xliff:g> ਬਾਕੀ"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਫ਼ੋਨ ਛੇਤੀ ਹੀ ਬੰਦ ਹੋ ਸਕਦਾ ਹੈ"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਟੈਬਲੈੱਟ ਛੇਤੀ ਹੀ ਬੰਦ ਹੋ ਸਕਦਾ ਹੈ"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਡੀਵਾਈਸ ਛੇਤੀ ਹੀ ਬੰਦ ਹੋ ਸਕਦਾ ਹੈ"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index c7deb5692494..45c44c6117ee 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Prywatny DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Wybierz tryb prywatnego DNS"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Wyłączony"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunistyczny"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nazwa hosta dostawcy prywatnego DNS"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Wpisz nazwę hosta dostawcy DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Pokaż opcje certyfikacji wyświetlacza bezprzewodowego"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"To jest funkcja eksperymentalna i może wpływać na działanie urządzenia."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Nadpisana przez <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Pozostało: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Jeszcze około <xliff:g id="TIME">%1$s</xliff:g> (na podstawie Twojego sposobu korzystania)"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> do pełnego naładowania"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Zostało <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Pozostało mniej niż <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – pozostało mniej niż <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> – pozostało ponad <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"pozostało ponad <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"telefon może się wkrótce wyłączyć"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tablet może się wkrótce wyłączyć"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"urządzenie może się wkrótce wyłączyć"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – pozostało około <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – jeszcze około <xliff:g id="TIME">%2$s</xliff:g> (na podstawie Twojego sposobu korzystania)"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – telefon może się wkrótce wyłączyć"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – tablet może się wkrótce wyłączyć"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – urządzenie może się wkrótce wyłączyć"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> do pełnego naładowania"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Nieznane"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Ładowanie"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 7693a5420979..bc9c82e491b4 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -135,7 +135,7 @@ <string name="tts_settings_title" msgid="1237820681016639683">"Conversão de texto em voz"</string> <string name="tts_default_rate_title" msgid="6030550998379310088">"Taxa de fala"</string> <string name="tts_default_rate_summary" msgid="4061815292287182801">"Velocidade em que o texto é falado"</string> - <string name="tts_default_pitch_title" msgid="6135942113172488671">"Frequência do som"</string> + <string name="tts_default_pitch_title" msgid="6135942113172488671">"Tom da fala"</string> <string name="tts_default_pitch_summary" msgid="1944885882882650009">"Afeta o tom da voz sintetizada"</string> <string name="tts_default_lang_title" msgid="8018087612299820556">"Idioma"</string> <string name="tts_lang_use_system" msgid="2679252467416513208">"Usar idioma do sistema"</string> @@ -157,7 +157,7 @@ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Iniciar configurações do mecanismo"</string> <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Mecanismo preferencial"</string> <string name="tts_general_section_title" msgid="4402572014604490502">"Gerais"</string> - <string name="tts_reset_speech_pitch_title" msgid="5789394019544785915">"Redefinir a frequência do som da fala"</string> + <string name="tts_reset_speech_pitch_title" msgid="5789394019544785915">"Redefinir o tom da fala"</string> <string name="tts_reset_speech_pitch_summary" msgid="8700539616245004418">"Redefinir o tom no qual o texto é falado para o padrão."</string> <string-array name="tts_rate_entries"> <item msgid="6695494874362656215">"Muito devagar"</item> @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS particular"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selecione o modo DNS particular"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Desativado"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunista"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nome do host do provedor de DNS particular"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Informe o nome do host do provedor de DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opções de certificação de Display sem fio"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Este recurso é experimental e pode afetar o desempenho."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Cerca de <xliff:g id="TIME">%1$s</xliff:g> restante(s)"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Cerca de <xliff:g id="TIME">%1$s</xliff:g> restante(s) com base no seu uso"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> restante(s) até a carga completa"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> restante(s)"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> restante(s)"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g>: menos de <xliff:g id="THRESHOLD">%2$s</xliff:g> restante(s)"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>mais de <xliff:g id="TIME_REMAINING">%2$s</xliff:g> restante(s)"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"mais de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> restante(s)"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"o smartphone pode ser desligado em breve"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"o tablet pode ser desligado em breve"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"o dispositivo pode ser desligado em breve"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - cerca de <xliff:g id="TIME">%2$s</xliff:g> restante(s)"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g>: cerca de <xliff:g id="TIME">%2$s</xliff:g> restante(s) com base no seu uso"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g>: o smartphone pode ser desligado em breve"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g>: o tablet pode ser desligado em breve"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g>: o dispositivo pode ser desligado em breve"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> restante(s) até a carga completa"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até a carga completa"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconhecido"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Carregando"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index a4987a1edc84..5b2ad1db61f0 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privado"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selecionar modo DNS privado"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Desativado"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunista"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nome de anfitrião do fornecedor DNS privado"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Introduza o nome de anfitrião do fornecedor DNS."</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opções da certificação de display sem fios"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta funcionalidade é experimental e pode afetar o desempenho."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Falta(m) cerca de <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Resta(m) cerca de <xliff:g id="TIME">%1$s</xliff:g> com base na sua utilização"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Falta(m) <xliff:g id="TIME">%1$s</xliff:g> para concluir o carregamento"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Resta(m) <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Resta(m) menos de <xliff:g id="THRESHOLD">%1$s</xliff:g>."</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – Resta(m) menos de <xliff:g id="THRESHOLD">%2$s</xliff:g>."</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> – Resta(m) mais de <xliff:g id="TIME_REMAINING">%2$s</xliff:g>."</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"Resta(m) mais de <xliff:g id="TIME_REMAINING">%1$s</xliff:g>."</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"O telemóvel poderá ser encerrado em breve."</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"O tablet poderá ser encerrado em breve."</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"O dispositivo poderá ser encerrado em breve."</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – falta(m) cerca de <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – resta(m) cerca de <xliff:g id="TIME">%2$s</xliff:g> com base na sua utilização"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – O telemóvel poderá ser encerrado em breve."</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – O tablet poderá ser encerrado em breve."</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – O dispositivo poderá ser encerrado em breve."</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"Falta(m) <xliff:g id="TIME">%1$s</xliff:g> para concluir o carregamento"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até ficar totalmente carregada"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconhecido"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"A carregar"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 7693a5420979..bc9c82e491b4 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -135,7 +135,7 @@ <string name="tts_settings_title" msgid="1237820681016639683">"Conversão de texto em voz"</string> <string name="tts_default_rate_title" msgid="6030550998379310088">"Taxa de fala"</string> <string name="tts_default_rate_summary" msgid="4061815292287182801">"Velocidade em que o texto é falado"</string> - <string name="tts_default_pitch_title" msgid="6135942113172488671">"Frequência do som"</string> + <string name="tts_default_pitch_title" msgid="6135942113172488671">"Tom da fala"</string> <string name="tts_default_pitch_summary" msgid="1944885882882650009">"Afeta o tom da voz sintetizada"</string> <string name="tts_default_lang_title" msgid="8018087612299820556">"Idioma"</string> <string name="tts_lang_use_system" msgid="2679252467416513208">"Usar idioma do sistema"</string> @@ -157,7 +157,7 @@ <string name="tts_engine_settings_button" msgid="1030512042040722285">"Iniciar configurações do mecanismo"</string> <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Mecanismo preferencial"</string> <string name="tts_general_section_title" msgid="4402572014604490502">"Gerais"</string> - <string name="tts_reset_speech_pitch_title" msgid="5789394019544785915">"Redefinir a frequência do som da fala"</string> + <string name="tts_reset_speech_pitch_title" msgid="5789394019544785915">"Redefinir o tom da fala"</string> <string name="tts_reset_speech_pitch_summary" msgid="8700539616245004418">"Redefinir o tom no qual o texto é falado para o padrão."</string> <string-array name="tts_rate_entries"> <item msgid="6695494874362656215">"Muito devagar"</item> @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS particular"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selecione o modo DNS particular"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Desativado"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunista"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nome do host do provedor de DNS particular"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Informe o nome do host do provedor de DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opções de certificação de Display sem fio"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Este recurso é experimental e pode afetar o desempenho."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Cerca de <xliff:g id="TIME">%1$s</xliff:g> restante(s)"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Cerca de <xliff:g id="TIME">%1$s</xliff:g> restante(s) com base no seu uso"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> restante(s) até a carga completa"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> restante(s)"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Menos de <xliff:g id="THRESHOLD">%1$s</xliff:g> restante(s)"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g>: menos de <xliff:g id="THRESHOLD">%2$s</xliff:g> restante(s)"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>mais de <xliff:g id="TIME_REMAINING">%2$s</xliff:g> restante(s)"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"mais de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> restante(s)"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"o smartphone pode ser desligado em breve"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"o tablet pode ser desligado em breve"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"o dispositivo pode ser desligado em breve"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - cerca de <xliff:g id="TIME">%2$s</xliff:g> restante(s)"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g>: cerca de <xliff:g id="TIME">%2$s</xliff:g> restante(s) com base no seu uso"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g>: o smartphone pode ser desligado em breve"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g>: o tablet pode ser desligado em breve"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g>: o dispositivo pode ser desligado em breve"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> restante(s) até a carga completa"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até a carga completa"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconhecido"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Carregando"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 1db376e7cbf7..4af2ba159de6 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privat"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selectați modul DNS privat"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Dezactivat"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunist"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Nume de gazdă al furnizorului de DNS privat"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Introduceți numele de gazdă al furnizorului de DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Afișați opțiunile pentru certificarea Ecran wireless"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Această funcție este experimentală și poate afecta performanțele."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Valoare înlocuită de <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Timp rămas: aproximativ <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"În baza utilizării, timpul aproximativ rămas este: <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Timp rămas până la încărcarea completă: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Timp rămas: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"a mai rămas mai puțin de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - a mai rămas mai puțin de <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> - a mai rămas mai mult de <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"a mai rămas mai mult de <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"telefonul să poate închide în curând"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tableta se poate închide în curând"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"dispozitivul se poate închide în curând"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - încă aproximativ <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – în baza utilizării, timpul aproximativ rămas este: <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - telefonul se poate închide în curând"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - tableta se poate închide în curând"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - dispozitivul se poate închide în curând"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"Timp rămas până la încărcarea completă: <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> până la încărcarea completă"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Necunoscut"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Se încarcă"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index be08b05ccae2..46d40aa4607c 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"ВЫКЛ"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Гибкий"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Имя хоста поставщика персонального DNS-сервера"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Введите имя хоста поставщика услуг DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Показывать параметры сертификации беспроводных мониторов"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Это экспериментальная функция, она может снизить производительность устройства."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Новая настройка: <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Осталось примерно <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Осталось примерно <xliff:g id="TIME">%1$s</xliff:g> при текущем уровне использования"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Ещё <xliff:g id="TIME">%1$s</xliff:g> до полной зарядки"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Осталось: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Осталось менее <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"Уровень заряда батареи <xliff:g id="LEVEL">%1$s</xliff:g>. Осталось менее <xliff:g id="THRESHOLD">%2$s</xliff:g>."</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"Уровень заряда батареи <xliff:g id="LEVEL">%1$s</xliff:g>. Осталось более <xliff:g id="TIME_REMAINING">%2$s</xliff:g>."</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"Осталось более <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"телефон скоро завершит работу"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"планшет скоро завершит работу"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"устройство скоро завершит работу"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – осталось примерно <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> (осталось примерно <xliff:g id="TIME">%2$s</xliff:g> при текущем уровне использования)"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"Уровень заряда батареи <xliff:g id="LEVEL">%1$s</xliff:g>. Телефон скоро завершит работу."</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"Уровень заряда батареи <xliff:g id="LEVEL">%1$s</xliff:g>. Планшет скоро завершит работу."</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"Уровень заряда батареи <xliff:g id="LEVEL">%1$s</xliff:g>. Устройство скоро завершит работу."</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"Идет зарядка"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 7d63e4776a18..1b387c395aca 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"ක්රියාවිරහිතයි"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"අවස්ථාවාදී"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"පුද්ගලික DNS සැපයුම්කරු සත්කාරක නම"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS සැපයුම්කරුගේ සත්කාරක නම ඇතුළු කරන්න"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"නොරැහැන් සංදර්ශක සහතිකය සඳහා විකල්ප පෙන්වන්න"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"මෙම විශේෂාංගය පරීක්ෂණාත්මක සහ ඇතැම් විට ක්රියාකාරිත්වයට බලපෑ හැක."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> මගින් ඉක්මවන ලදී"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"<xliff:g id="TIME">%1$s</xliff:g> පමණ ඉතිරියි"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"ඔබගේ භාවිතය මත පදනම්ව <xliff:g id="TIME">%1$s</xliff:g> පමණ ඉතිරිව ඇත"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"පූර්ණව ආරෝපණය වන තෙක් <xliff:g id="TIME">%1$s</xliff:g> ඉතිරියි"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"ඉතිරි <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>ට වඩා අඩුවෙන් ඉතිරිව ඇත"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="THRESHOLD">%2$s</xliff:g>ට වඩා අඩුවෙන් ඉතිරිව ඇත"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g><xliff:g id="TIME_REMAINING">%2$s</xliff:g>ට වඩා වැඩියෙන් ඉතිරිව ඇත"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>ට වඩා වැඩියෙන් ඉතිරිව ඇත"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"දුරකථනය ඉක්මනින් වැසිය හැකිය"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"ටැබ්ලට් උපාංගය ඉක්මනින් වැසිය හැකිය"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"උපාංගය ඉක්මනින් වැසිය හැකිය"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>ක් පමණ ඇත"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - ඔබගේ භාවිතය මත පදනම්ව <xliff:g id="TIME">%2$s</xliff:g> පමණ ඉතිරිව ඇත"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - දුරකථනය ඉක්මනින් වැසිය හැකිය"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - ටැබ්ලට් පරිගණකය ඉක්මනින් වැසිය හැකිය"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - උපාංගය ඉක්මනින් වැසිය හැකිය"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"ආරෝපණය වෙමින්"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index aa40fef7593c..6502d1a17f33 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Súkromné DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Výber súkromného režimu DNS"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Vypnuté"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Príležitostné"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Súkromný názov hostiteľa poskytovateľa DNS"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Zadajte názov hostiteľa poskytovateľa DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Zobraziť možnosti certifikácie bezdrôtového zobrazenia"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Funkcia je experimentálna a môže mať vplyv na výkonnosť."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Prekonané predvoľbou <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Približný zostávajúci čas: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Zostáva približne <xliff:g id="TIME">%1$s</xliff:g> v závislosti od intenzity využitia"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Zostávajúci čas do úplného nabitia: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Zostávajúci čas: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Zostáva menej ako <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – zostáva menej ako <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> – zostáva viac ako <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"zostáva viac ako <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"telefón sa môže čoskoro vypnúť"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tablet sa môže čoskoro vypnúť"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"zariadenie sa môže čoskoro vypnúť"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – približný zostávajúci čas: <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – zostáva približne <xliff:g id="TIME">%2$s</xliff:g> v závislosti od intenzity využitia"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – telefón sa môže čoskoro vypnúť"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – tablet sa môže čoskoro vypnúť"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – zariadenie sa môže čoskoro vypnúť"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"Zostávajúci čas do úplného nabitia: <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> do úplného nabitia"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Neznáme"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Nabíja sa"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 55e4308939ee..10dcb4d15fdf 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Zasebni strežnik DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Izbira načina zasebnega strežnika DNS"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Izklopljeno"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Priložnostno"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Ime gostitelja pri ponudniku zasebnega strežnika DNS"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Vnesite ime gostitelja pri ponudniku strežnika DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Pokaži možnosti za potrdilo brezžičnega zaslona"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"To je preskusna funkcija in lahko vpliva na učinkovitost delovanja."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Preglasila nastavitev: <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Še približno <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Glede na način uporabe imate na voljo še približno <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Še <xliff:g id="TIME">%1$s</xliff:g> do polne napolnjenosti"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Še <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Preostalo manj kot <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – preostalo manj kot <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> – preostalo več kot <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"preostalo več kot <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"telefon se bo morda kmalu zaustavil"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tablični računalnik se bo morda kmalu zaustavil"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"naprava se bo morda kmalu zaustavila"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – še približno <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – glede na način uporabe imate na voljo še približno <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – telefon se bo morda kmalu zaustavil"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – tablični računalnik se bo morda kmalu zaustavil"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – naprava se bo morda kmalu zaustavila"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"Še <xliff:g id="TIME">%1$s</xliff:g> do polne napolnjenosti"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Neznano"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Polnjenje"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 5524e2c5c737..738744169b54 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS-ja private"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Zgjidh modalitetin e DNS-së private"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Joaktiv"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunist"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Emri i pritësit të ofruesit të DNS-së private"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Fut emrin e pritësit të ofruesit të DNS-së"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Shfaq opsionet për certifikimin e ekranit valor"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ky funksion është eksperimental dhe mund të ndikojë në veprimtari."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Mbivendosur nga <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Rreth <xliff:g id="TIME">%1$s</xliff:g> të mbetura"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Rreth <xliff:g id="TIME">%1$s</xliff:g> të mbetura bazuar në përdorimin tënd"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> të mbetura deri në ngarkimin e plotë"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> të mbetura"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Më pak se <xliff:g id="THRESHOLD">%1$s</xliff:g> të mbetura"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Më pak se <xliff:g id="THRESHOLD">%2$s</xliff:g> të mbetura"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>më pak se <xliff:g id="TIME_REMAINING">%2$s</xliff:g> të mbetura"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"më shumë se <xliff:g id="TIME_REMAINING">%1$s</xliff:g> të mbetura"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"telefoni mund të fiket së shpejti"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tableti mund të fiket së shpejti"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"pajisja mund të fiket së shpejti"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - rreth <xliff:g id="TIME">%2$s</xliff:g> të mbetura"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - rreth <xliff:g id="TIME">%2$s</xliff:g> të mbetura bazuar në përdorimin tënd"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - telefoni mund të fiket së shpejti"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - tableti mund të fiket së shpejti"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - tableti mund të fiket së shpejti"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> të mbetura deri në ngarkimin e plotë"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të mbushet plotësisht"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"I panjohur"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Po ngarkohet"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index a01b0b6b71a1..c644eae48393 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"Искључено"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Оппортунистички"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Име хоста добављача услуге приватног DNS-а"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Унесите име хоста добављача услуге DNS-а"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Приказ опција за сертификацију бежичног екрана"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ова функција је експериментална и може да утиче на квалитет рада."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Замењује га <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Још око <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"На основу потрошње имате још отприлике <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> до потпуног пуњења"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Преостало време: <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Преостало је мање од <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – преостало је мање од <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> – преостало је више од <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"преостало је више од <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"телефон ће се ускоро искључити"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"таблет ће се ускоро искључити"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"уређај ће се ускоро искључити"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – остало је око <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – на основу потрошње имате још отприлике <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – телефон ће се ускоро искључити"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – таблет ће се ускоро искључити"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – уређај ће се ускоро искључити"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"Пуњење"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index d8c6e4d1c2b0..44875f52cc5f 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Privat DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Välj läget Privat DNS"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Av"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Opportunistisk"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Värdnamn för leverantör av privat DNS"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Ange värdnamn för DNS-leverantör"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Visa certifieringsalternativ för Wi-Fi-skärmdelning"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Den här funktionen är experimentell och kan påverka prestandan."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Har åsidosatts av <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Cirka <xliff:g id="TIME">%1$s</xliff:g> återstår"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Cirka <xliff:g id="TIME">%1$s</xliff:g> kvar utifrån din användning"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Batteriet är fulladdat om <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> kvar"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Mindre än <xliff:g id="THRESHOLD">%1$s</xliff:g> återstår"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – Mindre än <xliff:g id="THRESHOLD">%2$s</xliff:g> återstår"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>mer än <xliff:g id="TIME_REMAINING">%2$s</xliff:g> återstår"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"mer än <xliff:g id="TIME_REMAINING">%1$s</xliff:g> återstår"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"mobilen kan stängas av snart"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"surfplattan kan stängas av snart"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"enheten kan stängas av snart"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – cirka <xliff:g id="TIME">%2$s</xliff:g> kvar"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – cirka <xliff:g id="TIME">%2$s</xliff:g> kvar utifrån din användning"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – mobilen kan stängas av snart"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – surfplattan kan stängas av snart"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – enheten kan stängas av snart"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"Batteriet är fulladdat om <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> tills det är fulladdat"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Okänd"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Laddar"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index fd91e5b78422..6305fde95275 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS ya Faragha"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Chagua Hali ya DNS ya Faragha"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Imezimwa"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Inayojitokeza"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Jina la mpangishi wa huduma za DNS ya faragha"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Weka jina la mpangishi wa huduma za DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Onyesha chaguo za cheti cha kuonyesha pasiwaya"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Kipengele hiki ni cha majaribio na huenda kikaathiri utendaji wa kifaa chako."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Imetanguliwa na <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Zimesalia takribani <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Takriban <xliff:g id="TIME">%1$s</xliff:g> zimesalia kulingana na matumizi yako"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Imebaki <xliff:g id="TIME">%1$s</xliff:g> chaji ijae"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Zimesalia <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Zimesalia chini ya <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Zimesalia chini ya <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> zimesalia zaidi ya <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"zimesalia zaidi ya <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"simu inakaribia kuzimika"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"kompyuta kibao inakaribia kuzimika"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"kifaa kinakaribia kuzimika"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - imesalia takribani <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - takriban <xliff:g id="TIME">%2$s</xliff:g> zimesalia kulingana na matumizi yako"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - simu inakaribia kuzimika"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - kompyuta kibao inakaribia kuzimika"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - kifaa kinakaribia kuzimika"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"Imebaki <xliff:g id="TIME">%1$s</xliff:g> chaji ijae"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> hadi ijae chaji"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Haijulikani"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Inachaji"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index b417cfbfacd1..ff983b711448 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -76,7 +76,7 @@ <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ஆடியோ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ஆடியோ"</string> <string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"செவித்துணைக் கருவி"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"செவித்துணைக் கருவியுடன் இணைக்கப்பட்டிருக்கும்போது"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="7188282786730266159">"செவித்துணைக் கருவியுடன் இணைக்கப்பட்டிருக்கிறது"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="963376081347721598">"மீடியா ஆடியோவுடன் இணைக்கப்பட்டது"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="7661070206715520671">"மொபைல் ஆடியோவுடன் இணைக்கப்பட்டது"</string> <string name="bluetooth_opp_profile_summary_connected" msgid="2611913495968309066">"கோப்பைப் பரிமாற்றும் சேவையகத்துடன் இணைக்கப்பட்டது"</string> @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"ஆஃப்"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"சந்தர்ப்பவாதம்"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"தனிப்பட்ட DNS வழங்குநரின் ஹோஸ்ட் பெயர்"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS வழங்குநரின் ஹோஸ்ட் பெயரை உள்ளிடவும்"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"வயர்லெஸ் காட்சி சான்றுக்கான விருப்பங்களைக் காட்டு"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"இது சோதனை முறையிலான அம்சம், இது செயல்திறனைப் பாதிக்கலாம்."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> மூலம் மேலெழுதப்பட்டது"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"கிட்டத்தட்ட <xliff:g id="TIME">%1$s</xliff:g> உள்ளது"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"உபயோகத்தின் அடிப்படையில் கிட்டத்தட்ட <xliff:g id="TIME">%1$s</xliff:g> மீதமுள்ளது"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"முழு சார்ஜாக <xliff:g id="TIME">%1$s</xliff:g> ஆகும்"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> மீதமுள்ளது"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>க்கும் குறைவாகவே பயன்படுத்த முடியும்"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> பேட்டரி மட்டுமே உள்ளது, <xliff:g id="THRESHOLD">%2$s</xliff:g>க்கும் குறைவாகவே பயன்படுத்தமுடியும்"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"பேட்டரி <xliff:g id="LEVEL">%1$s</xliff:g> உள்ளது, <xliff:g id="TIME_REMAINING">%2$s</xliff:g>க்கு மேல் பயன்படுத்த முடியும்"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>க்கு மேல் பயன்படுத்த முடியும்"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"மொபைல் விரைவில் ஆஃப் ஆகக்கூடும்"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"டேப்லெட் விரைவில் ஆஃப் ஆகக்கூடும்"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"சாதனம் விரைவில் ஆஃப் ஆகக்கூடும்"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - கிட்டத்தட்ட <xliff:g id="TIME">%2$s</xliff:g> மீதமுள்ளது"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - உபயோகத்தின் அடிப்படையில் கிட்டத்தட்ட <xliff:g id="TIME">%2$s</xliff:g> மீதமுள்ளது"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - மொபைல் விரைவில் ஆஃப் ஆகக்கூடும்"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - டேப்லெட் விரைவில் ஆஃப் ஆகக்கூடும்"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - சாதனம் விரைவில் ஆஃப் ஆகக்கூடும்"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"சார்ஜ் ஆகிறது"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 99a35a34f0fd..bccb11e8c2bc 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"ఆఫ్"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"అవకాశవాదం"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"ప్రైవేట్ DNS ప్రదాత హోస్ట్పేరు"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS ప్రదాత యొక్క హోస్ట్పేరును నమోదు చేయండి"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"వైర్లెస్ ప్రదర్శన సర్టిఫికెట్ కోసం ఎంపికలను చూపు"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ఈ లక్షణం ప్రయోగాత్మకమైనది మరియు పనితీరుపై ప్రభావం చూపవచ్చు."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ద్వారా భర్తీ చేయబడింది"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"మిగిలి ఉన్న సమయం, <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"మీ వినియోగం ఆధారంగా సుమారు <xliff:g id="TIME">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"పూర్తిగా ఛార్జ్ కావడానికి <xliff:g id="TIME">%1$s</xliff:g> పడుతుంది"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> మిగిలి ఉంది"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> కంటే తక్కువ సమయం మిగిలి ఉంది"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="THRESHOLD">%2$s</xliff:g> కంటే తక్కువ సమయం మిగిలి ఉంది"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g><xliff:g id="TIME_REMAINING">%2$s</xliff:g> కంటే ఎక్కువ సమయం మిగిలి ఉంది"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> కంటే ఎక్కువ సమయం మిగిలి ఉంది"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"ఫోన్ త్వరలో షట్డౌన్ కావచ్చు"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"టాబ్లెట్ త్వరలో షట్డౌన్ కావచ్చు"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"పరికరం త్వరలో షట్డౌన్ కావచ్చు"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> పని చేస్తుంది"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - మీ వినియోగం ఆధారంగా సుమారు <xliff:g id="TIME">%2$s</xliff:g> సమయం మిగిలి ఉంది"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఫోన్ త్వరలో షట్డౌన్ కావచ్చు"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - టాబ్లెట్ త్వరలో షట్డౌన్ కావచ్చు"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - పరికరం త్వరలో షట్డౌన్ కావచ్చు"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"ఛార్జ్ అవుతోంది"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index aab479e32c58..2cedcfcf235d 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"ปิด"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"ที่ใช้โอกาส"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"ชื่อโฮสต์ของผู้ให้บริการ DNS ส่วนตัว"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"ป้อนชื่อโฮสต์ของผู้ให้บริการ DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"แสดงตัวเลือกสำหรับการรับรองการแสดงผล แบบไร้สาย"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ฟีเจอร์นี้เป็นแบบทดลองและอาจส่งผลต่อประสิทธิภาพการทำงาน"</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"แทนที่โดย <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"อีกประมาณ <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"เหลืออีกราว <xliff:g id="TIME">%1$s</xliff:g> ขึ้นอยู่กับการใช้งานของคุณ"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"อีก <xliff:g id="TIME">%1$s</xliff:g> จึงจะชาร์จเต็ม"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"เหลืออีก <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"เหลืออีกไม่ถึง <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - เหลืออีกไม่ถึง <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> - เหลือเวลามากกว่า <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"เหลือเวลามากกว่า <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"โทรศัพท์อาจปิดเครื่องในไม่ช้า"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"แท็บเล็ตอาจปิดเครื่องในไม่ช้า"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"อุปกรณ์อาจปิดเครื่องในไม่ช้า"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - เหลือเวลาประมาณ <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - เหลืออีกราว <xliff:g id="TIME">%2$s</xliff:g> ขึ้นอยู่กับการใช้งานของคุณ"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - โทรศัพท์อาจปิดเครื่องในไม่ช้า"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - แท็บเล็ตอาจปิดเครื่องในไม่ช้า"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - อุปกรณ์อาจปิดเครื่องในไม่ช้า"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"กำลังชาร์จ"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 6403213bd0d0..dd774d25c8df 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Pribadong DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Pumili ng Pribadong DNS Mode"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Naka-off"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Oportunista"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Hostname ng provider ng pribadong DNS"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Ilagay ang hostname ng DNS provider"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Ipakita ang mga opsyon para sa certification ng wireless display"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ang feature na ito ay pinag-eeksperimentuhan at maaaring makaapekto sa performance."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Na-override ng <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Humigit-kumulang <xliff:g id="TIME">%1$s</xliff:g> ang natitira"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Humigit-kumulang <xliff:g id="TIME">%1$s</xliff:g> ang natitira batay sa iyong paggamit"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> ang natitira bago makumpleto ang charge"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> pa"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Wala nang <xliff:g id="THRESHOLD">%1$s</xliff:g> ang natitira"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Wala nang <xliff:g id="THRESHOLD">%2$s</xliff:g> ang natitira"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>may mahigit <xliff:g id="TIME_REMAINING">%2$s</xliff:g> pa"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"may mahigit <xliff:g id="TIME_REMAINING">%1$s</xliff:g> pa"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"malapit nang mag-shut down ang telepono"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"malapit nang mag-shut down ang tablet"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"malapit nang mag-shut down ang device"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - humigit-kumulang <xliff:g id="TIME">%2$s</xliff:g> pa ang natitira"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - humigit-kumulang <xliff:g id="TIME">%2$s</xliff:g> ang natitira batay sa iyong paggamit"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - malapit nang mag-shut down ang telepono"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - malapit nang mag-shut down ang tablet"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - malapit nang mag-shut down ang device"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> ang natitira bago makumpleto ang charge"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> hanggang sa makumpleto ang charge"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Hindi Kilala"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Nagcha-charge"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 9fdeb4af5042..21707f406837 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Gizli DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Gizli DNS Modunu Seçin"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Kapalı"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Fırsatçı"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Gizli DNS sağlayıcının ana makine adı"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS sağlayıcının ana makine adını gir"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Kablosuz ekran sertifikası seçeneklerini göster"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Bu özellik deneyseldir ve performansı etkileyebilir."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> tarafından geçersiz kılındı"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Yaklaşık <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Kullanımınıza dayalı olarak yaklaşık <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Tam olarak şarj olmasına <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> kaldı"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"En fazla <xliff:g id="THRESHOLD">%1$s</xliff:g> kaldı"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - En fazla <xliff:g id="THRESHOLD">%2$s</xliff:g> kaldı"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> - en az <xliff:g id="TIME_REMAINING">%2$s</xliff:g> kaldı"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"en az <xliff:g id="TIME_REMAINING">%1$s</xliff:g> kaldı"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"telefon kısa süre içinde kapanabilir"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"tablet kısa süre içinde kapanabilir"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"cihaz kısa süre içinde kapanabilir"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - yaklaşık <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - kullanımınıza dayalı olarak yaklaşık <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - telefon kısa süre içinde kapanabilir"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - tablet kısa süre içinde kapanabilir"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - cihaz kısa süre içinde kapanabilir"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"Tam olarak şarj olmasına <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tam şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Bilinmiyor"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Şarj oluyor"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 1c85633fd351..d1e68e129900 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"Вимкнено"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Періодично доступно"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Ім’я хосту приватного постачальника послуг DNS"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Введіть ім’я хосту постачальника послуг DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Показати параметри сертифікації бездротового екрана"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Це експериментальна функція. Вона може вплинути на продуктивність."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Замінено на <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Залишилося близько <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"На основі використання залишилося близько <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"До повного зарядження залишилося <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Залишилося <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Залишилося менше ніж <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – залишилося менше ніж <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> – залишилося більше ніж <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"залишилося більше ніж <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"телефон може невдовзі вимкнутися"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"планшет може невдовзі вимкнутися"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"пристрій може невдовзі вимкнутися"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – ще <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> – на основі використання залишилося близько <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – телефон може невдовзі вимкнутися"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – планшет може невдовзі вимкнутися"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – пристрій може невдовзі вимкнутися"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"Заряджається"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 7c221d7fd38a..82686bdbbbba 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"آف"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"موقع پرست"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"نجی DNS فراہم کنندہ میزبان کا نام"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS فراہم کنندہ کے میزبان کا نام درج کریں"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"وائرلیس ڈسپلے سرٹیفیکیشن کیلئے اختیارات دکھائیں"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"یہ خصوصیت تجرباتی ہے اور اس کی وجہ سے کاکردگی متاثر ہو سکتی ہے۔"</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> کے ذریعہ منسوخ کردیا گیا"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"تقریبًا <xliff:g id="TIME">%1$s</xliff:g> باقی ہے"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"آپ کے استعمال کی بنیاد پر تقریباً <xliff:g id="TIME">%1$s</xliff:g> باقی ہے"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"پوری طرح چارج ہونے میں <xliff:g id="TIME">%1$s</xliff:g> باقی ہے"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> باقی ہے"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g> سے کم باقی ہے"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="THRESHOLD">%2$s</xliff:g> سے کم باقی ہے"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> <xliff:g id="TIME_REMAINING">%2$s</xliff:g> سے زیادہ باقی ہے"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g> سے زیادہ باقی ہے"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"فون جلد ہی بند ہو سکتا ہے"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"ٹیبلیٹ جلد ہی بند ہو سکتا ہے"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"آلہ جلد ہی بند ہو سکتا ہے"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - تقریباً <xliff:g id="TIME">%2$s</xliff:g> باقی ہے"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - آپ کے استعمال کی بنیاد پر تقریباً <xliff:g id="TIME">%2$s</xliff:g> باقی ہے"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - فون جلد ہی بند ہو سکتا ہے"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - ٹیبلیٹ جلد ہی بند ہو سکتا ہے"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - آلہ جلد ہی بند ہو سکتا ہے"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"چارج ہو رہا ہے"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 725d92f65b14..4043e98933cf 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"Shaxsiy DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Shaxsiy DNS rejimini tanlang"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"O‘chiq"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Moslashuvchan"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Shaxsiy DNS provayderining host nomi"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS provayderining host nomini kiriting"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Simsiz monitorlarni sertifikatlash parametrini ko‘rsatish"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Bu funksiya tajribaviy bo‘lib, u qurilma unumdorligiga ta’sir qilishi mumkin."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> bilan almashtirildi"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Taxminan <xliff:g id="TIME">%1$s</xliff:g> qoldi"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Joriy holatda taxminan <xliff:g id="TIME">%1$s</xliff:g> qoldi"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"To‘lishiga <xliff:g id="TIME">%1$s</xliff:g> qoldi"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> qoldi"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"<xliff:g id="THRESHOLD">%1$s</xliff:g>dan kamroq vaqt qoldi"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="THRESHOLD">%2$s</xliff:g>dan kamroq vaqt qoldi"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME_REMAINING">%2$s</xliff:g>dan ko‘proq vaqt qoldi"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"<xliff:g id="TIME_REMAINING">%1$s</xliff:g>dan ko‘proq vaqt qoldi"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"telefon tez orada o‘chib qolishi mumkin"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"planshet tez orada o‘chib qolishi mumkin"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"qurilma tez orada o‘chib qolishi mumkin"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> – taxminan <xliff:g id="TIME">%2$s</xliff:g> qoldi"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> (joriy holatda taxminan <xliff:g id="TIME">%2$s</xliff:g> qoldi)"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> – telefon tez orada o‘chib qolishi mumkin"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> – planshet tez orada o‘chib qolishi mumkin"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> – qurilma tez orada o‘chib qolishi mumkin"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"To‘lishiga <xliff:g id="TIME">%1$s</xliff:g> qoldi"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> ichida to‘ladi"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Noma’lum"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Quvvat olmoqda"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 74bac0aa42cc..f05a1a8dd59a 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS riêng tư"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Chọn chế độ DNS riêng tư"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Tắt"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Cơ hội"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Tên máy chủ của nhà cung cấp DNS riêng tư"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Nhập tên máy chủ của nhà cung cấp DNS"</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> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Tính năng này là tính năng thử nghiệm và có thể ảnh hưởng đến hoạt động."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Bị ghi đè bởi <xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Còn khoảng <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Còn khoảng <xliff:g id="TIME">%1$s</xliff:g> dựa trên mức sử dụng của bạn"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"Còn <xliff:g id="TIME">%1$s</xliff:g> cho tới khi được sạc đầy"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Còn lại <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Còn lại không đến <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Còn lại không đến <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> còn lại hơn <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"còn lại hơn <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"điện thoại có thể sắp tắt"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"máy tính bảng có thể sắp tắt"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"thiết bị có thể sắp tắt"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - còn khoảng <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - còn khoảng <xliff:g id="TIME">%2$s</xliff:g> dựa trên mức sử dụng của bạn"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - điện thoại có thể sắp tắt"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - máy tính bảng có thể sắp tắt"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - thiết bị có thể sắp tắt"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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">"Còn <xliff:g id="TIME">%1$s</xliff:g> cho tới khi được sạc đầy"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> cho tới khi được sạc đầy"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Không xác định"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Đang sạc"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 3590aceca3be..8c2342e879d2 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"关闭"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"随机"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"私人 DNS 提供商主机名"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"输入 DNS 提供商的主机名"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"显示无线显示认证选项"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"这是实验性功能,性能可能不稳定。"</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"已被“<xliff:g id="TITLE">%1$s</xliff:g>”覆盖"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"还剩大约 <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"根据您的使用情况,大约还可使用 <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"还需 <xliff:g id="TIME">%1$s</xliff:g>充满电"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"还可用 <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"剩余电池续航时间不到 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - 剩余电池续航时间不到 <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> - 剩余电池续航时间超过 <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"剩余电池续航时间超过 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"手机可能即将关机"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"平板电脑可能即将关机"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"设备可能即将关机"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - 大约还剩 <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - 根据您的使用情况,大约还可使用 <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - 手机可能即将关机"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - 平板电脑可能即将关机"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - 设备可能即将关机"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"正在充电"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index f2162f4be241..6f03f0ba7c1b 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"停用"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"隨機"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"不公開的網域名稱系統 (DNS) 供應商主機名稱"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"輸入網域名稱系統 (DNS) 供應商的主機名稱"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"顯示無線螢幕分享認證的選項"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"這是實驗性功能,效能尚待改善。"</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"已由「<xliff:g id="TITLE">%1$s</xliff:g>」覆寫"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"剩餘約 <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"根據您的使用情況,剩餘約 <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g>後就能充滿電"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"尚餘 <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"剩餘電量時間少於 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - 剩餘電量時間少於 <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> - 剩餘電量時間超過 <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"剩餘電量時間超過 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"手機可能即將關機"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"平板電腦可能即將關機"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"裝置可能即將關機"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - 剩餘約 <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - 根據您的使用情況,剩餘約 <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - 手機可能即將關機"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - 平板電腦可能即將關機"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - 裝置可能即將關機"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"充電中"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index fa3001aea94d..9856879e5dbb 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -220,7 +220,8 @@ <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="private_dns_mode_off" msgid="8236575187318721684">"停用"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"隨機"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"私人 DNS 供應商主機名稱"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"輸入 DNS 供應商的主機名稱"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"顯示無線螢幕分享認證的選項"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"這是一項實驗性功能,可能會對效能造成影響。"</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"已改為<xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"還有大約 <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"根據你的使用情形,剩餘時間大約還有 <xliff:g id="TIME">%1$s</xliff:g>"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"再過 <xliff:g id="TIME">%1$s</xliff:g>就能完成充電"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"還剩 <xliff:g id="TIME">%1$s</xliff:g>"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"電池可用時間不到 <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - 電池可用時間不到 <xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g> - 電池可用時間超過 <xliff:g id="TIME_REMAINING">%2$s</xliff:g>"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"電池可用時間超過 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"手機可能即將關機"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"平板電腦可能即將關機"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"裝置可能即將關機"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - 約剩 <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - 根據你的使用情形,剩餘時間大約還有 <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - 手機可能即將關機"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - 平板電腦可能即將關機"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - 裝置可能即將關機"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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_charging" msgid="1705179948350365604">"充電中"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 5a4180c454dc..64861ec868d4 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -220,7 +220,8 @@ <string name="select_private_dns_configuration_title" msgid="3700456559305263922">"I-DNS eyimfihlo"</string> <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Khetha imodi ye-DNS eyimfihlo"</string> <string name="private_dns_mode_off" msgid="8236575187318721684">"Kuvaliwe"</string> - <string name="private_dns_mode_opportunistic" msgid="7608409735589131766">"Amathuba"</string> + <!-- no translation found for private_dns_mode_opportunistic (8314986739896927399) --> + <skip /> <string name="private_dns_mode_provider" msgid="8354935160639360804">"Igama lomsingathi womhlinzeki we-DNS"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Faka igama lomsingathi womhlinzeki we-DNS"</string> <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Bonisa izinketho zokunikeza isitifiketi ukubukeka okungenantambo"</string> @@ -356,22 +357,41 @@ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Lesi sici esesilingo futhi singathinta ukusebenza."</string> <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Igitshezwe ngaphezulu yi-<xliff:g id="TITLE">%1$s</xliff:g>"</string> <string name="power_remaining_duration_only" msgid="845431008899029842">"Cishe u-<xliff:g id="TIME">%1$s</xliff:g> osele"</string> + <!-- no translation found for power_discharging_duration (6655472132189365839) --> + <skip /> <string name="power_remaining_duration_only_enhanced" msgid="5992456722677973678">"Cishe kusele okungu-<xliff:g id="TIME">%1$s</xliff:g> kusukela ekusetshenzisweni kwakho"</string> - <string name="power_remaining_charging_duration_only" msgid="1421102457410268886">"<xliff:g id="TIME">%1$s</xliff:g> kushiywe ishaja"</string> + <!-- no translation found for power_discharging_duration_enhanced (5726302316642148671) --> + <skip /> <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> esisele"</string> + <!-- no translation found for power_discharge_by_enhanced (8788299408879961465) --> + <skip /> + <!-- no translation found for power_discharge_by_only_enhanced (7692297898877104416) --> + <skip /> + <!-- no translation found for power_discharge_by (6427074755635635749) --> + <skip /> + <!-- no translation found for power_discharge_by_only (5888058889261108064) --> + <skip /> <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Kusele okungaphansi kunokungu-<xliff:g id="THRESHOLD">%1$s</xliff:g>"</string> - <string name="power_remaining_less_than_duration" msgid="7967078125657859046">"<xliff:g id="LEVEL">%1$s</xliff:g> - Kusele okungaphansi kokungu<xliff:g id="THRESHOLD">%2$s</xliff:g>"</string> - <string name="power_remaining_more_than_subtext" msgid="6846716609975752316">"<xliff:g id="LEVEL">%1$s</xliff:g>ngaphezu kokungu-<xliff:g id="TIME_REMAINING">%2$s</xliff:g> okusele"</string> - <string name="power_remaining_only_more_than_subtext" msgid="8884488700395194194">"ngaphezulu kokungu-<xliff:g id="TIME_REMAINING">%1$s</xliff:g> okusele"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="default" msgid="8168317165722752881">"ifoni ingacisha maduze"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="tablet" msgid="5957064378548718872">"ithebulethi ingacisha maduze"</string> - <string name="power_remaining_duration_only_shutdown_imminent" product="device" msgid="9055596817716471373">"idivayisi ingacisha maduze"</string> - <string name="power_discharging_duration" msgid="2843747179907396142">"<xliff:g id="LEVEL">%1$s</xliff:g> - cishe ngu-<xliff:g id="TIME">%2$s</xliff:g> osele"</string> - <string name="power_discharging_duration_enhanced" msgid="4401782117770255046">"<xliff:g id="LEVEL">%1$s</xliff:g> - cishe ngu-<xliff:g id="TIME">%2$s</xliff:g> osele kusukela ekusetshenzisweni kwakho"</string> - <string name="power_remaining_duration_shutdown_imminent" product="default" msgid="7679005631124015335">"<xliff:g id="LEVEL">%1$s</xliff:g> - ifoni ingacisha maduze"</string> - <string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="261050880878965621">"<xliff:g id="LEVEL">%1$s</xliff:g> - ithebulethi ingacisha maduze"</string> - <string name="power_remaining_duration_shutdown_imminent" product="device" msgid="2020049829798578618">"<xliff:g id="LEVEL">%1$s</xliff:g> - idivayisi ingacisha maduze"</string> + <!-- no translation found for power_remaining_less_than_duration (5751885147712659423) --> + <skip /> + <!-- no translation found for power_remaining_more_than_subtext (3176771815132876675) --> + <skip /> + <!-- no translation found for power_remaining_only_more_than_subtext (8931654680569617380) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (1181059207608751924) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2606370266981054691) --> + <skip /> + <!-- no translation found for power_remaining_duration_only_shutdown_imminent (2918084807716859985) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (3090926004324573908) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (7466484148515796216) --> + <skip /> + <!-- no translation found for power_remaining_duration_shutdown_imminent (603933521600231649) --> + <skip /> <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> kushiywe ishaja"</string> <string name="power_charging_duration" msgid="4676999980973411875">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> kuze ligcwale ngokuphelele"</string> <string name="battery_info_status_unknown" msgid="196130600938058547">"Akwaziwa"</string> <string name="battery_info_status_charging" msgid="1705179948350365604">"Iyashaja"</string> diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml index cd1be152b8d2..70f1cd81d7b9 100644 --- a/packages/SystemUI/res/layout/keyguard_status_bar.xml +++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml @@ -25,41 +25,48 @@ android:baselineAligned="false" > - <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch" - android:layout_width="@dimen/multi_user_switch_width_keyguard" + <LinearLayout + android:id="@+id/status_icon_area" + android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_alignParentEnd="true" - android:background="@drawable/ripple_drawable" - android:layout_marginEnd="@dimen/multi_user_switch_keyguard_margin"> - <ImageView android:id="@+id/multi_user_avatar" - android:layout_width="@dimen/multi_user_avatar_keyguard_size" - android:layout_height="@dimen/multi_user_avatar_keyguard_size" - android:layout_gravity="center" - android:scaleType="centerInside"/> - </com.android.systemui.statusbar.phone.MultiUserSwitch> - - <LinearLayout android:id="@+id/system_icons_super_container" - android:layout_width="wrap_content" - android:layout_height="@*android:dimen/quick_qs_total_height" - android:layout_toStartOf="@id/multi_user_switch" - android:layout_alignWithParentIfMissing="true" - android:layout_marginStart="@dimen/system_icons_super_container_margin_start" - android:paddingEnd="@dimen/system_icons_keyguard_padding_end"> + android:gravity="center_vertical|end" > <FrameLayout android:id="@+id/system_icons_container" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="@dimen/status_bar_height" - android:layout_gravity="center_vertical" - > + android:layout_weight="1" + android:layout_marginStart="@dimen/system_icons_super_container_margin_start" + android:gravity="center_vertical|end" + android:paddingEnd="@dimen/system_icons_keyguard_padding_end" > <include layout="@layout/system_icons" /> </FrameLayout> + + <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch" + android:layout_width="@dimen/multi_user_switch_width_keyguard" + android:layout_height="match_parent" + android:background="@drawable/ripple_drawable" + android:layout_marginEnd="@dimen/multi_user_switch_keyguard_margin"> + <ImageView android:id="@+id/multi_user_avatar" + android:layout_width="@dimen/multi_user_avatar_keyguard_size" + android:layout_height="@dimen/multi_user_avatar_keyguard_size" + android:layout_gravity="center" + android:scaleType="centerInside"/> + </com.android.systemui.statusbar.phone.MultiUserSwitch> </LinearLayout> + <Space + android:id="@+id/cutout_space_view" + android:layout_width="0dp" + android:layout_height="match_parent" + android:gravity="center" + android:visibility="gone" /> + <com.android.keyguard.CarrierText android:id="@+id/keyguard_carrier_text" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginStart="@dimen/keyguard_carrier_text_margin" - android:layout_toStartOf="@id/system_icons_super_container" + android:layout_toStartOf="@id/system_icons_container" android:gravity="center_vertical" android:ellipsize="marquee" android:textAppearance="?android:attr/textAppearanceSmall" diff --git a/packages/SystemUI/res/layout/menu_ime.xml b/packages/SystemUI/res/layout/menu_ime.xml index 24709478e076..5a0e76799e7c 100644 --- a/packages/SystemUI/res/layout/menu_ime.xml +++ b/packages/SystemUI/res/layout/menu_ime.xml @@ -16,15 +16,17 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" - android:layout_width="@dimen/navigation_side_padding" + android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_weight="0" > + <!-- Use width & height=match_parent for parent FrameLayout and buttons because they are placed + inside a view that has a size controlled by weight. Ensure weight is large enough to support + icon size. --> + <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu" - android:layout_width="@dimen/navigation_extra_key_width" + android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_marginEnd="2dp" android:scaleType="centerInside" systemui:keyCode="82" systemui:playSound="false" @@ -33,27 +35,24 @@ /> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/ime_switcher" - android:layout_width="@dimen/navigation_extra_key_width" + android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_marginEnd="2dp" android:visibility="invisible" android:contentDescription="@string/accessibility_ime_switch_button" android:scaleType="centerInside" /> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/rotate_suggestion" - android:layout_width="@dimen/navigation_extra_key_width" + android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_marginEnd="2dp" android:visibility="invisible" android:scaleType="centerInside" android:contentDescription="@string/accessibility_rotate_button" /> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/accessibility_button" - android:layout_width="@dimen/navigation_extra_key_width" + android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_marginEnd="2dp" android:visibility="invisible" android:contentDescription="@string/accessibility_accessibility_button" android:scaleType="centerInside" diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml index 3c248452cece..9bf787012dd3 100644 --- a/packages/SystemUI/res/layout/qs_footer_impl.xml +++ b/packages/SystemUI/res/layout/qs_footer_impl.xml @@ -127,7 +127,8 @@ android:id="@+id/qs_drag_handle_view" android:layout_width="24dp" android:layout_height="4dp" - android:layout_gravity="center" + android:layout_marginBottom="16dp" + android:layout_gravity="center_horizontal|bottom" android:background="@drawable/qs_footer_drag_handle" /> </com.android.systemui.qs.QSFooterImpl> diff --git a/packages/SystemUI/res/layout/signal_cluster_view.xml b/packages/SystemUI/res/layout/signal_cluster_view.xml index 25c87d3f981a..cfa372b8fc2e 100644 --- a/packages/SystemUI/res/layout/signal_cluster_view.xml +++ b/packages/SystemUI/res/layout/signal_cluster_view.xml @@ -115,27 +115,6 @@ android:layout_width="wrap_content" > </LinearLayout> - <FrameLayout - android:id="@+id/no_sims_combo" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:contentDescription="@string/accessibility_no_sims"> - <com.android.systemui.statusbar.AlphaOptimizedImageView - android:theme="?attr/lightIconTheme" - android:id="@+id/no_sims" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:src="@drawable/stat_sys_no_sims" - /> - <com.android.systemui.statusbar.AlphaOptimizedImageView - android:theme="?attr/darkIconTheme" - android:id="@+id/no_sims_dark" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:src="@drawable/stat_sys_no_sims" - android:alpha="0.0" - /> - </FrameLayout> <View android:id="@+id/wifi_airplane_spacer" android:layout_width="@dimen/status_bar_airplane_spacer_width" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 9245ac18a6b2..8cb68c099215 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -468,7 +468,7 @@ <string name="accessibility_casting">@string/quick_settings_casting</string> <!-- Content description of the work mode icon in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> - <string name="accessibility_work_mode">@string/quick_settings_work_mode_on_label</string> + <string name="accessibility_work_mode">@string/quick_settings_work_mode_label</string> <!-- Content description to tell the user that this button will remove an application from recents --> <string name="accessibility_recents_item_will_be_dismissed">Dismiss <xliff:g id="app" example="Calendar">%s</xliff:g>.</string> @@ -694,7 +694,8 @@ <string name="quick_settings_bluetooth_secondary_label_headset">Headset</string> <!-- QuickSettings: Bluetooth secondary label for an input/IO device being connected [CHAR LIMIT=20]--> <string name="quick_settings_bluetooth_secondary_label_input">Input</string> - + <!-- QuickSettings: Bluetooth secondary label shown when bluetooth is being enabled [CHAR LIMIT=NONE] --> + <string name="quick_settings_bluetooth_secondary_label_transient">Turning on…</string> <!-- QuickSettings: Brightness [CHAR LIMIT=NONE] --> <string name="quick_settings_brightness_label">Brightness</string> <!-- QuickSettings: Rotation Unlocked [CHAR LIMIT=NONE] --> @@ -743,6 +744,8 @@ <string name="quick_settings_wifi_on_label">Wi-Fi On</string> <!-- QuickSettings: Wifi detail panel, text when there are no items [CHAR LIMIT=NONE] --> <string name="quick_settings_wifi_detail_empty_text">No Wi-Fi networks available</string> + <!-- QuickSettings: Wifi secondary label shown when the wifi is being enabled [CHAR LIMIT=NONE] --> + <string name="quick_settings_wifi_secondary_label_transient">Turning on…</string> <!-- QuickSettings: Cast title [CHAR LIMIT=NONE] --> <string name="quick_settings_cast_title">Cast</string> <!-- QuickSettings: Cast detail panel, status text when casting [CHAR LIMIT=NONE] --> @@ -776,7 +779,7 @@ <!-- QuickSettings: Hotspot. [CHAR LIMIT=NONE] --> <string name="quick_settings_hotspot_label">Hotspot</string> <!-- QuickSettings: Hotspot. Secondary label shown when the hotspot is being enabled [CHAR LIMIT=NONE] --> - <string name="quick_settings_hotspot_secondary_label_transient">Turning on...</string> + <string name="quick_settings_hotspot_secondary_label_transient">Turning on…</string> <!-- QuickSettings: Hotspot: Secondary label for how many devices are connected to the hotspot [CHAR LIMIT=NONE] --> <plurals name="quick_settings_hotspot_secondary_label_num_devices"> <item quantity="one">%d device</item> @@ -804,10 +807,7 @@ the top of their phone's screen. This is a label for a toggle to turn the work profile on and off. "Work profile" means a separate profile on a user's phone that's specifically for their work apps and managed by their company. "Work" is used as an adjective. [CHAR LIMIT=NONE] --> - <string name="quick_settings_work_mode_on_label">Work profile</string> - <!-- QuickSettings: This is a label for a toggle to turn the work profile on and off and this is - shown when work profile is off. [CHAR LIMIT=NONE] --> - <string name="quick_settings_work_mode_off_label">Notifications & apps are off</string> + <string name="quick_settings_work_mode_label">Work profile</string> <!-- QuickSettings: Label for the toggle to activate Night display (renamed "Night Light" with title caps). [CHAR LIMIT=20] --> <string name="quick_settings_night_display_label">Night Light</string> <!-- QuickSettings: Secondary text for when the Night Light will be enabled at sunset. [CHAR LIMIT=20] --> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index 2f28c814c76b..1aad27f99cdc 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -432,12 +432,4 @@ public class ActivityManagerWrapper { return false; } } - - /** - * @return whether screen pinning is enabled. - * @deprecated See {@link #isScreenPinningEnabled} - */ - public boolean isLockToAppEnabled() { - return isScreenPinningEnabled(); - } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java index 5b49e67f5492..a9c80c62e998 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java @@ -109,4 +109,16 @@ public class WindowManagerWrapper { Log.w(TAG, "Failed to end prolonged animations: ", e); } } + + /** + * Enable or disable haptic feedback on the navigation bar buttons. + */ + public void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) { + try { + WindowManagerGlobal.getWindowManagerService() + .setNavBarVirtualKeyHapticFeedbackEnabled(enabled); + } catch (RemoteException e) { + Log.w(TAG, "Failed to enable or disable navigation bar button haptics: ", e); + } + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index 4c2aa6355813..2adb2869e0f6 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -162,8 +162,8 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe mRow.addView(button); PendingIntent pendingIntent = null; - if (rc.getContentIntent() != null) { - pendingIntent = rc.getContentIntent().getAction(); + if (rc.getPrimaryAction() != null) { + pendingIntent = rc.getPrimaryAction().getAction(); } mClickActions.put(button, pendingIntent); diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java index 3c666e4b11cc..041af0e47119 100644 --- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java @@ -36,6 +36,7 @@ import android.view.SurfaceControl; import com.android.systemui.OverviewProxyService.OverviewProxyListener; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent; +import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.GraphicBufferCompat; @@ -201,15 +202,18 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis mConnectionBackoffAttempts = 0; mLauncherComponentName = ComponentName .unflattenFromString(context.getString(R.string.config_overviewServiceComponent)); - mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback); // Listen for the package update changes. - IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); - filter.addDataScheme("package"); - filter.addDataSchemeSpecificPart(mLauncherComponentName.getPackageName(), - PatternMatcher.PATTERN_LITERAL); - filter.addAction(Intent.ACTION_PACKAGE_CHANGED); - mContext.registerReceiver(mLauncherAddedReceiver, filter); + if (SystemServicesProxy.getInstance(context) + .isSystemUser(mDeviceProvisionedController.getCurrentUser())) { + mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback); + IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); + filter.addDataScheme("package"); + filter.addDataSchemeSpecificPart(mLauncherComponentName.getPackageName(), + PatternMatcher.PATTERN_LITERAL); + filter.addAction(Intent.ACTION_PACKAGE_CHANGED); + mContext.registerReceiver(mLauncherAddedReceiver, filter); + } } public void startConnectionToCurrentUser() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index 04dbb88adde0..24ddafcdd1c6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -66,6 +66,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile { protected static final boolean DEBUG = Log.isLoggable("Tile", Log.DEBUG); private static final long DEFAULT_STALE_TIMEOUT = 10 * DateUtils.MINUTE_IN_MILLIS; + protected static final Object ARG_SHOW_TRANSIENT_ENABLING = new Object(); protected final QSHost mHost; protected final Context mContext; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index 2a9a3818a746..3b79db7aa3ab 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -90,6 +90,8 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { protected void handleClick() { // Secondary clicks are header clicks, just toggle. final boolean isEnabled = mState.value; + // Immediately enter transient enabling state when turning bluetooth on. + refreshState(isEnabled ? null : ARG_SHOW_TRANSIENT_ENABLING); mController.setBluetoothEnabled(!isEnabled); } @@ -118,9 +120,10 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { @Override protected void handleUpdateState(BooleanState state, Object arg) { - final boolean enabled = mController.isBluetoothEnabled(); + final boolean transientEnabling = arg == ARG_SHOW_TRANSIENT_ENABLING; + final boolean enabled = transientEnabling || mController.isBluetoothEnabled(); final boolean connected = mController.isBluetoothConnected(); - state.isTransient = mController.isBluetoothConnecting() + state.isTransient = transientEnabling || mController.isBluetoothConnecting() || mController.getBluetoothState() == BluetoothAdapter.STATE_TURNING_ON; state.dualTarget = true; state.value = enabled; @@ -129,7 +132,6 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { } state.slash.isSlashed = !enabled; state.label = mContext.getString(R.string.quick_settings_bluetooth_label); - if (enabled) { if (connected) { state.icon = new BluetoothConnectedTileIcon(); @@ -155,8 +157,7 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { state.state = Tile.STATE_INACTIVE; } - state.secondaryLabel = getSecondaryLabel(enabled, connected); - + state.secondaryLabel = getSecondaryLabel(enabled, connected, state.isTransient); state.dualLabelContentDescription = mContext.getResources().getString( R.string.accessibility_quick_settings_open_settings, getTileLabel()); state.expandedAccessibilityClassName = Switch.class.getName(); @@ -169,9 +170,13 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { * * @param enabled whether bluetooth is enabled * @param connected whether there's a device connected via bluetooth + * @param isTransient whether bluetooth is currently in a transient state turning on */ @Nullable - private String getSecondaryLabel(boolean enabled, boolean connected) { + private String getSecondaryLabel(boolean enabled, boolean connected, boolean isTransient) { + if (isTransient) { + return mContext.getString(R.string.quick_settings_bluetooth_secondary_label_transient); + } final CachedBluetoothDevice lastDevice = mController.getLastDevice(); if (enabled && connected && lastDevice != null) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java index 080e320802e6..d675f5e17117 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -21,7 +21,6 @@ import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; import android.os.UserManager; - import android.provider.Settings.Global; import android.service.quicksettings.Tile; import android.widget.Switch; @@ -29,9 +28,9 @@ import android.widget.Switch; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.plugins.qs.QSTile.AirplaneBooleanState; import com.android.systemui.qs.GlobalSetting; import com.android.systemui.qs.QSHost; -import com.android.systemui.plugins.qs.QSTile.AirplaneBooleanState; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.HotspotController; @@ -96,10 +95,12 @@ public class HotspotTile extends QSTileImpl<AirplaneBooleanState> { @Override protected void handleClick() { - final boolean isEnabled = (Boolean) mState.value; + final boolean isEnabled = mState.value; if (!isEnabled && mAirplaneMode.getValue() != 0) { return; } + // Immediately enter transient enabling state when turning hotspot on. + refreshState(isEnabled ? null : ARG_SHOW_TRANSIENT_ENABLING); mController.setHotspotEnabled(!isEnabled); } @@ -110,12 +111,13 @@ public class HotspotTile extends QSTileImpl<AirplaneBooleanState> { @Override protected void handleUpdateState(AirplaneBooleanState state, Object arg) { + final boolean transientEnabling = arg == ARG_SHOW_TRANSIENT_ENABLING; if (state.slash == null) { state.slash = new SlashState(); } final int numConnectedDevices; - final boolean isTransient = mController.isHotspotTransient(); + final boolean isTransient = transientEnabling || mController.isHotspotTransient(); checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_TETHERING); if (arg instanceof CallbackInfo) { @@ -123,7 +125,7 @@ public class HotspotTile extends QSTileImpl<AirplaneBooleanState> { state.value = info.enabled; numConnectedDevices = info.numConnectedDevices; } else { - state.value = mController.isHotspotEnabled(); + state.value = transientEnabling || mController.isHotspotEnabled(); numConnectedDevices = mController.getNumConnectedDevices(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java index 540d28e5fa79..3ad3940c2ed2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -117,7 +117,10 @@ public class WifiTile extends QSTileImpl<SignalState> { protected void handleClick() { // Secondary clicks are header clicks, just toggle. mState.copyTo(mStateBeforeClick); - mController.setWifiEnabled(!mState.value); + boolean wifiEnabled = mState.value; + // Immediately enter transient state when turning on wifi. + refreshState(wifiEnabled ? null : ARG_SHOW_TRANSIENT_ENABLING); + mController.setWifiEnabled(!wifiEnabled); } @Override @@ -141,11 +144,13 @@ public class WifiTile extends QSTileImpl<SignalState> { @Override protected void handleUpdateState(SignalState state, Object arg) { if (DEBUG) Log.d(TAG, "handleUpdateState arg=" + arg); - CallbackInfo cb = (CallbackInfo) arg; - if (cb == null) { + final CallbackInfo cb; + if (arg != null && arg instanceof CallbackInfo) { + cb = (CallbackInfo) arg; + } else { cb = mSignalCallback.mInfo; } - + boolean transientEnabling = arg == ARG_SHOW_TRANSIENT_ENABLING; boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null); boolean wifiNotConnected = (cb.wifiSignalIconId > 0) && (cb.enabledDesc == null); boolean enabledChanging = state.value != cb.enabled; @@ -158,14 +163,16 @@ public class WifiTile extends QSTileImpl<SignalState> { state.slash.rotation = 6; } state.slash.isSlashed = false; + boolean isTransient = transientEnabling || cb.isTransient; + state.secondaryLabel = getSecondaryLabel(isTransient); state.state = Tile.STATE_ACTIVE; state.dualTarget = true; - state.value = cb.enabled; + state.value = transientEnabling || cb.enabled; state.activityIn = cb.enabled && cb.activityIn; state.activityOut = cb.enabled && cb.activityOut; final StringBuffer minimalContentDescription = new StringBuffer(); final Resources r = mContext.getResources(); - if (cb.isTransient) { + if (isTransient) { state.icon = ResourceIcon.get(R.drawable.ic_signal_wifi_transient_animation); state.label = r.getString(R.string.quick_settings_wifi_label); } else if (!state.value) { @@ -197,6 +204,12 @@ public class WifiTile extends QSTileImpl<SignalState> { state.expandedAccessibilityClassName = Switch.class.getName(); } + private CharSequence getSecondaryLabel(boolean isTransient) { + return isTransient + ? mContext.getString(R.string.quick_settings_wifi_secondary_label_transient) + : null; + } + @Override public int getMetricsCategory() { return MetricsEvent.QS_WIFI; @@ -277,8 +290,6 @@ public class WifiTile extends QSTileImpl<SignalState> { } } - ; - protected class WifiDetailAdapter implements DetailAdapter, NetworkController.AccessPointController.AccessPointCallback, QSDetailItems.Callback { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java index e098fd876735..5bab3e8f39a4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java @@ -21,12 +21,11 @@ import android.provider.Settings; import android.service.quicksettings.Tile; import android.widget.Switch; -import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.qs.QSHost; import com.android.systemui.plugins.qs.QSTile.BooleanState; +import com.android.systemui.qs.QSHost; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.phone.ManagedProfileController; @@ -83,11 +82,15 @@ public class WorkModeTile extends QSTileImpl<BooleanState> implements @Override public CharSequence getTileLabel() { - return mContext.getString(R.string.quick_settings_work_mode_on_label); + return mContext.getString(R.string.quick_settings_work_mode_label); } @Override protected void handleUpdateState(BooleanState state, Object arg) { + if (!isAvailable()) { + onManagedProfileRemoved(); + } + if (state.slash == null) { state.slash = new SlashState(); } @@ -103,13 +106,12 @@ public class WorkModeTile extends QSTileImpl<BooleanState> implements state.slash.isSlashed = false; state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_work_mode_on); - state.label = mContext.getString(R.string.quick_settings_work_mode_on_label); } else { state.slash.isSlashed = true; state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_work_mode_off); - state.label = mContext.getString(R.string.quick_settings_work_mode_off_label); } + state.label = mContext.getString(R.string.quick_settings_work_mode_label); state.expandedAccessibilityClassName = Switch.class.getName(); state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java index cb6e5a6e0bae..0f5e71e5235a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java @@ -73,9 +73,7 @@ public class SignalClusterView extends LinearLayout implements NetworkController private final NetworkController mNetworkController; private final SecurityController mSecurityController; - private boolean mNoSimsVisible = false; private boolean mVpnVisible = false; - private boolean mSimDetected; private int mVpnIconId = 0; private int mLastVpnIconId = -1; private boolean mEthernetVisible = false; @@ -86,7 +84,6 @@ public class SignalClusterView extends LinearLayout implements NetworkController private int mLastWifiStrengthId = -1; private boolean mWifiIn; private boolean mWifiOut; - private int mLastWifiActivityId = -1; private boolean mIsAirplaneMode = false; private int mAirplaneIconId = 0; private int mLastAirplaneIconId = -1; @@ -99,8 +96,7 @@ public class SignalClusterView extends LinearLayout implements NetworkController private final Rect mTintArea = new Rect(); ViewGroup mEthernetGroup, mWifiGroup; - View mNoSimsCombo; - ImageView mVpn, mEthernet, mWifi, mAirplane, mNoSims, mEthernetDark, mWifiDark, mNoSimsDark; + ImageView mVpn, mEthernet, mWifi, mAirplane, mEthernetDark, mWifiDark; ImageView mWifiActivityIn; ImageView mWifiActivityOut; View mWifiAirplaneSpacer; @@ -203,9 +199,6 @@ public class SignalClusterView extends LinearLayout implements NetworkController mWifiActivityIn = findViewById(R.id.wifi_in); mWifiActivityOut= findViewById(R.id.wifi_out); mAirplane = findViewById(R.id.airplane); - mNoSims = findViewById(R.id.no_sims); - mNoSimsDark = findViewById(R.id.no_sims_dark); - mNoSimsCombo = findViewById(R.id.no_sims_combo); mWifiAirplaneSpacer = findViewById(R.id.wifi_airplane_spacer); mWifiSignalSpacer = findViewById(R.id.wifi_signal_spacer); mMobileSignalGroup = findViewById(R.id.mobile_signal_group); @@ -224,11 +217,6 @@ public class SignalClusterView extends LinearLayout implements NetworkController } mVpn.setImageDrawable(new ScalingDrawableWrapper(mVpn.getDrawable(), mIconScaleFactor)); - - mNoSims.setImageDrawable( - new ScalingDrawableWrapper(mNoSims.getDrawable(), mIconScaleFactor)); - mNoSimsDark.setImageDrawable( - new ScalingDrawableWrapper(mNoSimsDark.getDrawable(), mIconScaleFactor)); } @Override @@ -333,9 +321,7 @@ public class SignalClusterView extends LinearLayout implements NetworkController @Override public void setNoSims(boolean show, boolean simDetected) { - mNoSimsVisible = show && !mBlockMobile; - mSimDetected = simDetected; - apply(); + // Noop. Status bar no longer shows no sim icon. } @Override @@ -545,38 +531,13 @@ public class SignalClusterView extends LinearLayout implements NetworkController mWifiAirplaneSpacer.setVisibility(View.GONE); } - if (((anyMobileVisible && firstMobileTypeId != 0) || mNoSimsVisible) && mWifiVisible) { + if ((anyMobileVisible && firstMobileTypeId != 0) && mWifiVisible) { mWifiSignalSpacer.setVisibility(View.VISIBLE); } else { mWifiSignalSpacer.setVisibility(View.GONE); } - if (mNoSimsVisible) { - mIconLogger.onIconShown(SLOT_MOBILE); - mNoSimsCombo.setVisibility(View.VISIBLE); - if (!Objects.equals(mSimDetected, mNoSimsCombo.getTag())) { - mNoSimsCombo.setTag(mSimDetected); - if (mSimDetected) { - SignalDrawable d = new SignalDrawable(mNoSims.getContext()); - d.setDarkIntensity(0); - mNoSims.setImageDrawable(d); - mNoSims.setImageLevel(SignalDrawable.getEmptyState(4)); - - SignalDrawable dark = new SignalDrawable(mNoSims.getContext()); - dark.setDarkIntensity(1); - mNoSimsDark.setImageDrawable(dark); - mNoSimsDark.setImageLevel(SignalDrawable.getEmptyState(4)); - } else { - mNoSims.setImageResource(R.drawable.stat_sys_no_sims); - mNoSimsDark.setImageResource(R.drawable.stat_sys_no_sims); - } - } - } else { - mIconLogger.onIconHidden(SLOT_MOBILE); - mNoSimsCombo.setVisibility(View.GONE); - } - - boolean anythingVisible = mNoSimsVisible || mWifiVisible || mIsAirplaneMode + boolean anythingVisible = mWifiVisible || mIsAirplaneMode || anyMobileVisible || mVpnVisible || mEthernetVisible; setPaddingRelative(0, 0, anythingVisible ? mEndPadding : mEndPaddingNothingVisible, 0); } @@ -613,9 +574,6 @@ public class SignalClusterView extends LinearLayout implements NetworkController setTint(mVpn, DarkIconDispatcher.getTint(mTintArea, mVpn, mIconTint)); setTint(mAirplane, DarkIconDispatcher.getTint(mTintArea, mAirplane, mIconTint)); applyDarkIntensity( - DarkIconDispatcher.getDarkIntensity(mTintArea, mNoSims, mDarkIntensity), - mNoSims, mNoSimsDark); - applyDarkIntensity( DarkIconDispatcher.getDarkIntensity(mTintArea, mWifi, mDarkIntensity), mWifi, mWifiDark); setTint(mWifiActivityIn, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index 2375b6a07bf6..c5269ad3d147 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -26,10 +26,13 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.TypedValue; +import android.view.DisplayCutout; import android.view.View; import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; import android.view.ViewTreeObserver; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; @@ -59,6 +62,10 @@ import com.android.systemui.statusbar.policy.UserSwitcherController; public class KeyguardStatusBarView extends RelativeLayout implements BatteryStateChangeCallback, OnUserInfoChangedListener, ConfigurationListener { + private static final int LAYOUT_NONE = 0; + private static final int LAYOUT_CUTOUT = 1; + private static final int LAYOUT_NO_CUTOUT = 2; + private boolean mBatteryCharging; private boolean mKeyguardUserSwitcherShowing; private boolean mBatteryListening; @@ -78,6 +85,10 @@ public class KeyguardStatusBarView extends RelativeLayout private View mSystemIconsContainer; private TintedIconManager mIconManager; + private View mCutoutSpace; + private ViewGroup mStatusIconArea; + private int mLayoutState = LAYOUT_NONE; + public KeyguardStatusBarView(Context context, AttributeSet attrs) { super(context, attrs); } @@ -85,12 +96,13 @@ public class KeyguardStatusBarView extends RelativeLayout @Override protected void onFinishInflate() { super.onFinishInflate(); - mSystemIconsSuperContainer = findViewById(R.id.system_icons_super_container); mSystemIconsContainer = findViewById(R.id.system_icons_container); mMultiUserSwitch = findViewById(R.id.multi_user_switch); mMultiUserAvatar = findViewById(R.id.multi_user_avatar); mCarrierLabel = findViewById(R.id.keyguard_carrier_text); mBatteryView = mSystemIconsContainer.findViewById(R.id.battery); + mCutoutSpace = findViewById(R.id.cutout_space_view); + mStatusIconArea = findViewById(R.id.status_icon_area); loadDimens(); updateUserSwitcher(); @@ -106,6 +118,7 @@ public class KeyguardStatusBarView extends RelativeLayout R.dimen.multi_user_avatar_keyguard_size); mMultiUserAvatar.setLayoutParams(lp); + // Multi-user switch lp = (MarginLayoutParams) mMultiUserSwitch.getLayoutParams(); lp.width = getResources().getDimensionPixelSize( R.dimen.multi_user_switch_width_keyguard); @@ -113,21 +126,21 @@ public class KeyguardStatusBarView extends RelativeLayout R.dimen.multi_user_switch_keyguard_margin)); mMultiUserSwitch.setLayoutParams(lp); - lp = (MarginLayoutParams) mSystemIconsSuperContainer.getLayoutParams(); + // System icons + lp = (MarginLayoutParams) mStatusIconArea.getLayoutParams(); lp.height = getResources().getDimensionPixelSize( - com.android.internal.R.dimen.quick_qs_total_height); - lp.setMarginStart(getResources().getDimensionPixelSize( - R.dimen.system_icons_super_container_margin_start)); - mSystemIconsSuperContainer.setLayoutParams(lp); - mSystemIconsSuperContainer.setPaddingRelative(mSystemIconsSuperContainer.getPaddingStart(), - mSystemIconsSuperContainer.getPaddingTop(), - getResources().getDimensionPixelSize(R.dimen.system_icons_keyguard_padding_end), - mSystemIconsSuperContainer.getPaddingBottom()); + com.android.internal.R.dimen.status_bar_height); lp = (MarginLayoutParams) mSystemIconsContainer.getLayoutParams(); lp.height = getResources().getDimensionPixelSize( R.dimen.status_bar_height); + lp.setMarginStart(getResources().getDimensionPixelSize( + R.dimen.system_icons_super_container_margin_start)); mSystemIconsContainer.setLayoutParams(lp); + mSystemIconsContainer.setPaddingRelative(mSystemIconsContainer.getPaddingStart(), + mSystemIconsContainer.getPaddingTop(), + getResources().getDimensionPixelSize(R.dimen.system_icons_keyguard_padding_end), + mSystemIconsContainer.getPaddingBottom()); // Respect font size setting. mCarrierLabel.setTextSize(TypedValue.COMPLEX_UNIT_PX, @@ -153,13 +166,13 @@ public class KeyguardStatusBarView extends RelativeLayout } private void updateVisibilities() { - if (mMultiUserSwitch.getParent() != this && !mKeyguardUserSwitcherShowing) { + if (mMultiUserSwitch.getParent() != mStatusIconArea && !mKeyguardUserSwitcherShowing) { if (mMultiUserSwitch.getParent() != null) { getOverlay().remove(mMultiUserSwitch); } - addView(mMultiUserSwitch, 0); - } else if (mMultiUserSwitch.getParent() == this && mKeyguardUserSwitcherShowing) { - removeView(mMultiUserSwitch); + mStatusIconArea.addView(mMultiUserSwitch, 0); + } else if (mMultiUserSwitch.getParent() == mStatusIconArea && mKeyguardUserSwitcherShowing) { + mStatusIconArea.removeView(mMultiUserSwitch); } if (mKeyguardUserSwitcher == null) { // If we have no keyguard switcher, the screen width is under 600dp. In this case, @@ -175,8 +188,8 @@ public class KeyguardStatusBarView extends RelativeLayout } private void updateSystemIconsLayoutParams() { - RelativeLayout.LayoutParams lp = - (LayoutParams) mSystemIconsSuperContainer.getLayoutParams(); + LinearLayout.LayoutParams lp = + (LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams(); // If the avatar icon is gone, we need to have some end margin to display the system icons // correctly. int baseMarginEnd = mMultiUserSwitch.getVisibility() == View.GONE @@ -186,8 +199,86 @@ public class KeyguardStatusBarView extends RelativeLayout baseMarginEnd; if (marginEnd != lp.getMarginEnd()) { lp.setMarginEnd(marginEnd); - mSystemIconsSuperContainer.setLayoutParams(lp); + mSystemIconsContainer.setLayoutParams(lp); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + updateLayoutConsideringCutout(); + setSignalClusterLayoutWidth(); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + private void updateLayoutConsideringCutout() { + DisplayCutout dc = getRootWindowInsets().getDisplayCutout(); + if (dc == null) { + updateLayoutParamsNoCutout(); + } else { + updateLayoutParamsForCutout(dc); + } + } + + private void updateLayoutParamsNoCutout() { + if (mLayoutState == LAYOUT_NO_CUTOUT) { + return; + } + mLayoutState = LAYOUT_NO_CUTOUT; + + if (mCutoutSpace != null) { + mCutoutSpace.setVisibility(View.GONE); + } + + RelativeLayout.LayoutParams lp = (LayoutParams) mCarrierLabel.getLayoutParams(); + lp.addRule(RelativeLayout.START_OF, R.id.status_icon_area); + + lp = (LayoutParams) mStatusIconArea.getLayoutParams(); + lp.removeRule(RelativeLayout.RIGHT_OF); + lp.width = LayoutParams.WRAP_CONTENT; + + LinearLayout.LayoutParams llp = + (LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams(); + llp.setMarginStart(getResources().getDimensionPixelSize( + R.dimen.system_icons_super_container_margin_start)); + } + + private void updateLayoutParamsForCutout(DisplayCutout dc) { + if (mLayoutState == LAYOUT_CUTOUT) { + return; + } + mLayoutState = LAYOUT_CUTOUT; + + if (mCutoutSpace == null) { + updateLayoutParamsNoCutout(); + } + + mCutoutSpace.setVisibility(View.VISIBLE); + RelativeLayout.LayoutParams lp = (LayoutParams) mCutoutSpace.getLayoutParams(); + lp.width = dc.getBoundingRect().width(); + lp.height = dc.getBoundingRect().height(); + lp.addRule(RelativeLayout.CENTER_IN_PARENT); + + lp = (LayoutParams) mCarrierLabel.getLayoutParams(); + lp.addRule(RelativeLayout.START_OF, R.id.cutout_space_view); + + lp = (LayoutParams) mStatusIconArea.getLayoutParams(); + lp.addRule(RelativeLayout.RIGHT_OF, R.id.cutout_space_view); + lp.width = LayoutParams.MATCH_PARENT; + + LinearLayout.LayoutParams llp = + (LinearLayout.LayoutParams) mSystemIconsContainer.getLayoutParams(); + llp.setMarginStart(0); + } + + //TODO: Something is setting signal_cluster to MATCH_PARENT. Why? + private void setSignalClusterLayoutWidth() { + View signalCluster = findViewById(R.id.signal_cluster); + if (signalCluster == null) { + return; } + + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) signalCluster.getLayoutParams(); + lp.width = LinearLayout.LayoutParams.WRAP_CONTENT; } public void setListening(boolean listening) { @@ -265,20 +356,21 @@ public class KeyguardStatusBarView extends RelativeLayout animateNextLayoutChange(); } updateVisibilities(); + updateLayoutConsideringCutout(); updateSystemIconsLayoutParams(); } private void animateNextLayoutChange() { - final int systemIconsCurrentX = mSystemIconsSuperContainer.getLeft(); - final boolean userSwitcherVisible = mMultiUserSwitch.getParent() == this; + final int systemIconsCurrentX = mSystemIconsContainer.getLeft(); + final boolean userSwitcherVisible = mMultiUserSwitch.getParent() == mStatusIconArea; getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { getViewTreeObserver().removeOnPreDrawListener(this); boolean userSwitcherHiding = userSwitcherVisible - && mMultiUserSwitch.getParent() != KeyguardStatusBarView.this; - mSystemIconsSuperContainer.setX(systemIconsCurrentX); - mSystemIconsSuperContainer.animate() + && mMultiUserSwitch.getParent() != mStatusIconArea; + mSystemIconsContainer.setX(systemIconsCurrentX); + mSystemIconsContainer.animate() .translationX(0) .setDuration(400) .setStartDelay(userSwitcherHiding ? 300 : 0) @@ -315,8 +407,8 @@ public class KeyguardStatusBarView extends RelativeLayout public void setVisibility(int visibility) { super.setVisibility(visibility); if (visibility != View.VISIBLE) { - mSystemIconsSuperContainer.animate().cancel(); - mSystemIconsSuperContainer.setTranslationX(0); + mSystemIconsContainer.animate().cancel(); + mSystemIconsContainer.setTranslationX(0); mMultiUserSwitch.animate().cancel(); mMultiUserSwitch.setAlpha(1f); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java index 1c361caa4a1a..d61d6e291b0e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java @@ -260,9 +260,15 @@ public class LightBarController implements BatteryController.BatteryStateChangeC pw.print(" mScrimAlpha="); pw.print(mScrimAlpha); pw.print(" mScrimAlphaBelowThreshold="); pw.println(mScrimAlphaBelowThreshold); pw.println(); - pw.println(" StatusBarTransitionsController:"); - mStatusBarIconController.getTransitionsController().dump(fd, pw, args); - pw.println(); + + LightBarTransitionsController transitionsController = + mStatusBarIconController.getTransitionsController(); + if (transitionsController != null) { + pw.println(" StatusBarTransitionsController:"); + transitionsController.dump(fd, pw, args); + pw.println(); + } + if (mNavigationBarController != null) { pw.println(" NavigationBarTransitionsController:"); mNavigationBarController.dump(fd, pw, args); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java index 4d99a46e2321..27798203e4fe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java @@ -52,6 +52,21 @@ public class LockscreenGestureLogger { EventLogTags.writeSysuiLockscreenGesture(safeLookup(gesture), length, velocity); } + /** + * Record the location of a swipe gesture, expressed as percentages of the whole screen + * @param category the action + * @param xPercent x-location / width * 100 + * @param yPercent y-location / height * 100 + */ + public void writeAtFractionalPosition( + int category, int xPercent, int yPercent, int rotation) { + mMetricsLogger.write(mLogMaker.setCategory(category) + .setType(MetricsEvent.TYPE_ACTION) + .addTaggedData(MetricsEvent.FIELD_GESTURE_X_PERCENT, xPercent) + .addTaggedData(MetricsEvent.FIELD_GESTURE_Y_PERCENT, yPercent) + .addTaggedData(MetricsEvent.FIELD_DEVICE_ROTATION, rotation)); + } + private int safeLookup(int gesture) { Integer value = mLegacyMap.get(gesture); if (value == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 0ed69e66b03e..da042d2bb8ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -46,7 +46,6 @@ import android.database.ContentObserver; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.AnimatedVectorDrawable; -import android.graphics.drawable.Drawable; import android.inputmethodservice.InputMethodService; import android.os.Binder; import android.os.Bundle; @@ -88,6 +87,7 @@ import com.android.systemui.fragments.FragmentHostManager.FragmentListener; import com.android.systemui.recents.Recents; import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CommandQueue.Callbacks; @@ -165,6 +165,8 @@ public class NavigationBarFragment extends Fragment implements Callbacks { public void onConnectionChanged(boolean isConnected) { mNavigationBarView.onOverviewProxyConnectionChanged(isConnected); updateScreenPinningGestures(); + WindowManagerWrapper.getInstance() + .setNavBarVirtualKeyHapticFeedbackEnabled(!isConnected); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java index a4daed92cabf..6a1ed51e85b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java @@ -150,8 +150,8 @@ public class NavigationBarGestureHelper implements TunerService.Tunable, Gesture } public boolean onInterceptTouchEvent(MotionEvent event) { - if (ActivityManagerWrapper.getInstance().isScreenPinningActive() - || mStatusBar.isKeyguardShowing()) { + if (mNavigationBarView.inScreenPinning() || mStatusBar.isKeyguardShowing() + || !mStatusBar.isPresenterFullyCollapsed()) { return false; } @@ -170,7 +170,7 @@ public class NavigationBarGestureHelper implements TunerService.Tunable, Gesture } } boolean handledByQuickscrub = mQuickScrubController.onInterceptTouchEvent(event); - if (mStatusBar.isPresenterFullyCollapsed() && !handledByQuickscrub) { + if (!handledByQuickscrub) { // Proxy motion events until we start intercepting for quickscrub proxyMotionEvents(event); } @@ -184,18 +184,17 @@ public class NavigationBarGestureHelper implements TunerService.Tunable, Gesture } public boolean onTouchEvent(MotionEvent event) { - if (ActivityManagerWrapper.getInstance().isScreenPinningActive() - || mStatusBar.isKeyguardShowing()) { + if (mNavigationBarView.inScreenPinning() || mStatusBar.isKeyguardShowing() + || !mStatusBar.isPresenterFullyCollapsed()) { return false; } // The same down event was just sent on intercept and therefore can be ignored here boolean ignoreProxyDownEvent = event.getAction() == MotionEvent.ACTION_DOWN && mOverviewProxyService.getProxy() != null; - boolean result = mStatusBar.isPresenterFullyCollapsed() - && (mQuickScrubController.onTouchEvent(event) + boolean result = mQuickScrubController.onTouchEvent(event) || ignoreProxyDownEvent - || proxyMotionEvents(event)); + || proxyMotionEvents(event); result |= mRecentsAnimationStarted; if (mDockWindowEnabled) { result |= handleDockWindowEvent(event); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 6daabede7f32..9c2a0e5f96b9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -342,7 +342,7 @@ public abstract class PanelView extends FrameLayout { onTrackingStarted(); } if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()) { - startOpening(); + startOpening(event); } break; @@ -417,7 +417,7 @@ public abstract class PanelView extends FrameLayout { return !mGestureWaitForTouchSlop || mTracking; } - private void startOpening() {; + private void startOpening(MotionEvent event) { runPeekAnimation(INITIAL_OPENING_PEEK_DURATION, getOpeningHeight(), false /* collapseWhenFinished */); notifyBarPanelExpansionChanged(); @@ -425,6 +425,18 @@ public abstract class PanelView extends FrameLayout { AsyncTask.execute(() -> mVibrator.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_TICK, false))); } + + //TODO: keyguard opens QS a different way; log that too? + + // Log the position of the swipe that opened the panel + float width = mStatusBar.getDisplayWidth(); + float height = mStatusBar.getDisplayHeight(); + int rot = mStatusBar.getRotation(); + + mLockscreenGestureLogger.writeAtFractionalPosition(MetricsEvent.ACTION_PANEL_VIEW_EXPAND, + (int) (event.getX() / width * 100), + (int) (event.getY() / height * 100), + rot); } protected abstract float getOpeningHeight(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index d8d388c4be08..d0a434f8857f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.phone; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.app.AlarmManager; import android.app.WallpaperManager; @@ -362,7 +361,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, * * The expansion fraction is tied to the scrim opacity. * - * @param fraction From 0 to 1 where 0 means collapse and 1 expanded. + * @param fraction From 0 to 1 where 0 means collapsed and 1 expanded. */ public void setPanelExpansion(float fraction) { if (mExpansionFraction != fraction) { @@ -381,8 +380,25 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, if (mPinnedHeadsUpCount != 0) { updateHeadsUpScrim(false); } - updateScrim(false /* animate */, mScrimInFront, mCurrentInFrontAlpha); - updateScrim(false /* animate */, mScrimBehind, mCurrentBehindAlpha); + + setOrAdaptCurrentAnimation(mScrimBehind); + setOrAdaptCurrentAnimation(mScrimInFront); + } + } + + private void setOrAdaptCurrentAnimation(View scrim) { + if (!isAnimating(scrim)) { + updateScrimColor(scrim, getCurrentScrimAlpha(scrim), getCurrentScrimTint(scrim)); + } else { + ValueAnimator previousAnimator = (ValueAnimator) scrim.getTag(TAG_KEY_ANIM); + float alpha = getCurrentScrimAlpha(scrim); + float previousEndValue = (Float) scrim.getTag(TAG_END_ALPHA); + float previousStartValue = (Float) scrim.getTag(TAG_START_ALPHA); + float relativeDiff = alpha - previousEndValue; + float newStartValue = previousStartValue + relativeDiff; + scrim.setTag(TAG_START_ALPHA, newStartValue); + scrim.setTag(TAG_END_ALPHA, alpha); + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); } } @@ -523,14 +539,14 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, setScrimAlpha(mScrimInFront, alpha); } - private void setScrimAlpha(View scrim, float alpha) { + private void setScrimAlpha(ScrimView scrim, float alpha) { if (alpha == 0f) { scrim.setClickable(false); } else { // Eat touch events (unless dozing). scrim.setClickable(!(mState == ScrimState.AOD)); } - updateScrim(mAnimateChange, scrim, alpha); + updateScrim(scrim, alpha); } private void updateScrimColor(View scrim, float alpha, int tint) { @@ -554,21 +570,17 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, dispatchScrimsVisible(); } - private int getCurrentScrimTint(View scrim) { - return scrim == mScrimInFront ? mCurrentInFrontTint : mCurrentBehindTint; - } - private void startScrimAnimation(final View scrim, float current) { ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); final int initialScrimTint = scrim instanceof ScrimView ? ((ScrimView) scrim).getTint() : Color.TRANSPARENT; anim.addUpdateListener(animation -> { + final float startAlpha = (Float) scrim.getTag(TAG_START_ALPHA); final float animAmount = (float) animation.getAnimatedValue(); - final int finalScrimTint = scrim == mScrimInFront ? - mCurrentInFrontTint : mCurrentBehindTint; - float finalScrimAlpha = scrim == mScrimInFront ? - mCurrentInFrontAlpha : mCurrentBehindAlpha; - float alpha = MathUtils.lerp(current, finalScrimAlpha, animAmount); + final int finalScrimTint = getCurrentScrimTint(scrim); + final float finalScrimAlpha = getCurrentScrimAlpha(scrim); + float alpha = MathUtils.lerp(startAlpha, finalScrimAlpha, animAmount); + alpha = MathUtils.constrain(alpha, 0f, 1f); int tint = ColorUtils.blendARGB(initialScrimTint, finalScrimTint, animAmount); updateScrimColor(scrim, alpha, tint); dispatchScrimsVisible(); @@ -579,12 +591,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - final int finalScrimTint = scrim == mScrimInFront ? - mCurrentInFrontTint : mCurrentBehindTint; - float finalScrimAlpha = scrim == mScrimInFront ? - mCurrentInFrontAlpha : mCurrentBehindAlpha; - updateScrimColor(scrim, finalScrimAlpha, finalScrimTint); - if (mKeyguardFadingOutInProgress) { mKeyguardFadeoutAnimation = null; mKeyguardFadingOutInProgress = false; @@ -600,12 +606,42 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } } }); - anim.start(); if (mAnimateKeyguardFadingOut) { mKeyguardFadingOutInProgress = true; mKeyguardFadeoutAnimation = anim; } + + // Cache alpha values because we might want to update this animator in the future if + // the user expands the panel while the animation is still running. + scrim.setTag(TAG_START_ALPHA, current); + scrim.setTag(TAG_END_ALPHA, getCurrentScrimAlpha(scrim)); + scrim.setTag(TAG_KEY_ANIM, anim); + anim.start(); + } + + private float getCurrentScrimAlpha(View scrim) { + if (scrim == mScrimInFront) { + return mCurrentInFrontAlpha; + } else if (scrim == mScrimBehind) { + return mCurrentBehindAlpha; + } else if (scrim == mHeadsUpScrim) { + return calculateHeadsUpAlpha(); + } else { + throw new IllegalArgumentException("Unknown scrim view"); + } + } + + private int getCurrentScrimTint(View scrim) { + if (scrim == mScrimInFront) { + return mCurrentInFrontTint; + } else if (scrim == mScrimBehind) { + return mCurrentBehindTint; + } else if (scrim == mHeadsUpScrim) { + return Color.TRANSPARENT; + } else { + throw new IllegalArgumentException("Unknown scrim view"); + } } protected Interpolator getInterpolator() { @@ -693,7 +729,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } private void updateHeadsUpScrim(boolean animate) { - updateScrim(animate, mHeadsUpScrim, calculateHeadsUpAlpha()); + if (animate) { + mAnimationDuration = ANIMATION_DURATION; + cancelAnimator((ValueAnimator) mHeadsUpScrim.getTag(TAG_KEY_ANIM)); + startScrimAnimation(mHeadsUpScrim, mHeadsUpScrim.getAlpha()); + } else { + setOrAdaptCurrentAnimation(mHeadsUpScrim); + } } @VisibleForTesting @@ -701,32 +743,28 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mOnAnimationFinished = onAnimationFinished; } - private void updateScrim(boolean animate, View scrim, float alpha) { - final float currentAlpha = scrim instanceof ScrimView ? ((ScrimView) scrim).getViewAlpha() - : scrim.getAlpha(); + private void updateScrim(ScrimView scrim, float alpha) { + final float currentAlpha = scrim.getViewAlpha(); ValueAnimator previousAnimator = ViewState.getChildTag(scrim, TAG_KEY_ANIM); - float animEndValue = -1; if (previousAnimator != null) { - if (animate || alpha == currentAlpha) { + if (mAnimateChange) { // We are not done yet! Defer calling the finished listener. - if (animate) { - mDeferFinishedListener = true; - } - cancelAnimator(previousAnimator); - mDeferFinishedListener = false; - } else { - animEndValue = ViewState.getChildTag(scrim, TAG_END_ALPHA); + mDeferFinishedListener = true; } + // Previous animators should always be cancelled. Not doing so would cause + // overlap, especially on states that don't animate, leading to flickering, + // and in the worst case, an internal state that doesn't represent what + // transitionTo requested. + cancelAnimator(previousAnimator); + mDeferFinishedListener = false; } if (mPendingFrameCallback != null) { // Display is off and we're waiting. - cancelAnimator(previousAnimator); return; } else if (mBlankScreen) { // Need to blank the display before continuing. - cancelAnimator(previousAnimator); blankDisplay(); return; } else if (!mScreenBlankingCallbackCalled) { @@ -744,36 +782,17 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } final ScrimView scrimView = scrim instanceof ScrimView ? (ScrimView) scrim : null; - final boolean wantsAlphaUpdate = alpha != currentAlpha && alpha != animEndValue; + final boolean wantsAlphaUpdate = alpha != currentAlpha; final boolean wantsTintUpdate = scrimView != null && scrimView.getTint() != getCurrentScrimTint(scrimView); if (wantsAlphaUpdate || wantsTintUpdate) { - if (animate) { - final float fromAlpha = scrimView == null ? scrim.getAlpha() - : scrimView.getViewAlpha(); - startScrimAnimation(scrim, fromAlpha); - scrim.setTag(TAG_START_ALPHA, currentAlpha); - scrim.setTag(TAG_END_ALPHA, alpha); + if (mAnimateChange) { + startScrimAnimation(scrim, currentAlpha); } else { - if (previousAnimator != null) { - float previousStartValue = ViewState.getChildTag(scrim, TAG_START_ALPHA); - float previousEndValue = ViewState.getChildTag(scrim, TAG_END_ALPHA); - // we need to increase all animation keyframes of the previous animator by the - // relative change to the end value - PropertyValuesHolder[] values = previousAnimator.getValues(); - float relativeDiff = alpha - previousEndValue; - float newStartValue = previousStartValue + relativeDiff; - newStartValue = Math.max(0, Math.min(1.0f, newStartValue)); - values[0].setFloatValues(newStartValue, alpha); - scrim.setTag(TAG_START_ALPHA, newStartValue); - scrim.setTag(TAG_END_ALPHA, alpha); - previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); - } else { - // update the alpha directly - updateScrimColor(scrim, alpha, getCurrentScrimTint(scrim)); - onFinished(); - } + // update the alpha directly + updateScrimColor(scrim, alpha, getCurrentScrimTint(scrim)); + onFinished(); } } else { onFinished(); 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 86e618e4690c..408cd85c29e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -2834,6 +2834,18 @@ public class StatusBar extends SystemUI implements DemoMode, return mDisplayMetrics.density; } + float getDisplayWidth() { + return mDisplayMetrics.widthPixels; + } + + float getDisplayHeight() { + return mDisplayMetrics.heightPixels; + } + + int getRotation() { + return mDisplay.getRotation(); + } + public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned, boolean dismissShade) { startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java index dba89479cf98..dab28d65a5a9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java @@ -69,10 +69,14 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + int width = MeasureSpec.getSize(widthMeasureSpec); + int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED); + final int count = getChildCount(); // Measure all children so that they report the correct width for (int i = 0; i < count; i++) { - measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec); + measureChild(getChildAt(i), widthSpec, heightMeasureSpec); } } @@ -99,7 +103,8 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { int childCount = getChildCount(); // Underflow === don't show content until that index int firstUnderflowIndex = -1; - if (DEBUG) android.util.Log.d(TAG, "calculateIconTransitions: start=" + translationX); + if (DEBUG) android.util.Log.d(TAG, "calculateIconTransitions: start=" + translationX + + " width=" + width); //TODO: Dots for (int i = childCount - 1; i >= 0; i--) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java index 8792b4f3cc4c..d6d0673e2c0b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java @@ -140,7 +140,6 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof if (DEBUG) Log.d(TAG, "Starting tethering"); mConnectivityManager.startTethering( ConnectivityManager.TETHERING_WIFI, false, callback); - fireHotspotChangedCallback(isHotspotEnabled()); } else { mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java index e7b802d0067f..aec05cd9218f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java @@ -232,12 +232,7 @@ public class KeyButtonView extends ImageView implements ButtonInterface { performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); } mIsPressed = true; - if (isProxyConnected) { - // Provide small vibration for quick step or immediate down feedback - AsyncTask.execute(() -> - mVibrator.vibrate(VibrationEffect - .get(VibrationEffect.EFFECT_TICK, false))); - } else { + if (!isProxyConnected) { playSoundEffect(SoundEffectConstants.CLICK); setPressed(mIsPressed); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 168d8d36290d..3c9cca2da91d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -77,7 +77,7 @@ public class ScrimControllerTest extends SysuiTestCase { mLightBarController = mock(LightBarController.class); mScrimBehind = new ScrimView(getContext()); mScrimInFront = new ScrimView(getContext()); - mHeadsUpScrim = mock(View.class); + mHeadsUpScrim = new View(getContext()); mWakeLock = mock(WakeLock.class); mAlarmManager = mock(AlarmManager.class); mAlwaysOnEnabled = true; @@ -384,6 +384,56 @@ public class ScrimControllerTest extends SysuiTestCase { testConservesNotificationDensity(3 /* count */, ScrimController.GRADIENT_SCRIM_ALPHA_BUSY); } + @Test + public void testHeadsUpScrimOpacity() { + mScrimController.setPanelExpansion(0f); + mScrimController.onHeadsUpPinned(null /* row */); + mScrimController.finishAnimationsImmediately(); + + Assert.assertNotEquals("Heads-up scrim should be visible", 0f, + mHeadsUpScrim.getAlpha(), 0.01f); + + mScrimController.onHeadsUpUnPinned(null /* row */); + mScrimController.finishAnimationsImmediately(); + + Assert.assertEquals("Heads-up scrim should have disappeared", 0f, + mHeadsUpScrim.getAlpha(), 0.01f); + } + + @Test + public void testHeadsUpScrimCounting() { + mScrimController.setPanelExpansion(0f); + mScrimController.onHeadsUpPinned(null /* row */); + mScrimController.onHeadsUpPinned(null /* row */); + mScrimController.onHeadsUpPinned(null /* row */); + mScrimController.finishAnimationsImmediately(); + + Assert.assertNotEquals("Heads-up scrim should be visible", 0f, + mHeadsUpScrim.getAlpha(), 0.01f); + + mScrimController.onHeadsUpUnPinned(null /* row */); + mScrimController.finishAnimationsImmediately(); + + Assert.assertEquals("Heads-up scrim should only disappear when counter reaches 0", 1f, + mHeadsUpScrim.getAlpha(), 0.01f); + + mScrimController.onHeadsUpUnPinned(null /* row */); + mScrimController.onHeadsUpUnPinned(null /* row */); + mScrimController.finishAnimationsImmediately(); + Assert.assertEquals("Heads-up scrim should have disappeared", 0f, + mHeadsUpScrim.getAlpha(), 0.01f); + } + + @Test + public void testNoHeadsUpScrimExpanded() { + mScrimController.setPanelExpansion(1f); + mScrimController.onHeadsUpPinned(null /* row */); + mScrimController.finishAnimationsImmediately(); + + Assert.assertEquals("Heads-up scrim should not be visible when shade is expanded", 0f, + mHeadsUpScrim.getAlpha(), 0.01f); + } + /** * Conserves old notification density after leaving state and coming back. * @@ -458,6 +508,7 @@ public class ScrimControllerTest extends SysuiTestCase { // Force finish all animations. endAnimation(mScrimBehind, TAG_KEY_ANIM); endAnimation(mScrimInFront, TAG_KEY_ANIM); + endAnimation(mHeadsUpScrim, TAG_KEY_ANIM); if (!animationFinished[0]) { throw new IllegalStateException("Animation never finished"); @@ -470,7 +521,7 @@ public class ScrimControllerTest extends SysuiTestCase { return wasCancelled; } - private void endAnimation(ScrimView scrimView, int tag) { + private void endAnimation(View scrimView, int tag) { Animator animator = (Animator) scrimView.getTag(tag); if (animator != null) { animator.end(); diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 6f31b0a22445..7819a80ec790 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -5348,6 +5348,27 @@ message MetricsEvent { // OS: P RECENT_LOCATION_REQUESTS_ALL = 1325; + // FIELD: The x-location of a swipe gesture, conveyed as percent of total width + // CATEGORY: GLOBAL_SYSTEM_UI + // OS: P + FIELD_GESTURE_X_PERCENT = 1326; + + // FIELD: The y-location of a swipe gesture, conveyed as percent of total width + // CATEGORY: GLOBAL_SYSTEM_UI + // OS: P + FIELD_GESTURE_Y_PERCENT = 1327; + + // ACTION: Expand the notification panel while unlocked + // CATEGORY: GLOBAL_SYSTEM_UI + // OS: P + ACTION_PANEL_VIEW_EXPAND = 1328; + + + // FIELD: Rotation of the device + // CATEGORY: GLOBAL_SYSTEM_UI + // OS: P + FIELD_DEVICE_ROTATION = 1329; + // ---- End P Constants, all P constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index b32be736533b..52d0e08e4e7f 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -24,8 +24,9 @@ #include <utils/misc.h> #include <inttypes.h> +#include <android-base/macros.h> #include <androidfw/Asset.h> -#include <androidfw/AssetManager.h> +#include <androidfw/AssetManager2.h> #include <androidfw/ResourceTypes.h> #include <android-base/macros.h> @@ -1664,18 +1665,22 @@ nFileA3DCreateFromAssetStream(JNIEnv *_env, jobject _this, jlong con, jlong nati static jlong nFileA3DCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path) { - AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); + Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); - if (asset == nullptr) { - return 0; + std::unique_ptr<Asset> asset; + { + ScopedLock<AssetManager2> locked_mgr(*mgr); + asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; + } } - jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset); + jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset.release()); return id; } @@ -1752,22 +1757,25 @@ static jlong nFontCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path, jfloat fontSize, jint dpi) { - AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr); + Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(_env, _assetMgr); if (mgr == nullptr) { return 0; } AutoJavaStringToUTF8 str(_env, _path); - Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER); - if (asset == nullptr) { - return 0; + std::unique_ptr<Asset> asset; + { + ScopedLock<AssetManager2> locked_mgr(*mgr); + asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER); + if (asset == nullptr) { + return 0; + } } jlong id = (jlong)(uintptr_t)rsFontCreateFromMemory((RsContext)con, str.c_str(), str.length(), fontSize, dpi, asset->getBuffer(false), asset->getLength()); - delete asset; return id; } diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index 53c9ecb81ad8..15f3a2362043 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -61,8 +61,8 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; - import com.android.internal.util.function.pooled.PooledLambda; + import libcore.util.EmptyArray; import org.xmlpull.v1.XmlPullParser; @@ -1961,10 +1961,16 @@ public class AppOpsService extends IAppOpsService.Stub { int mode; int packageUid; int nonpackageUid; + final static Binder sBinder = new Binder(); + IBinder mToken; Shell(IAppOpsService iface, AppOpsService internal) { mInterface = iface; mInternal = internal; + try { + mToken = mInterface.getToken(sBinder); + } catch (RemoteException e) { + } } @Override @@ -2149,6 +2155,10 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println("AppOps service (appops) commands:"); pw.println(" help"); pw.println(" Print this help text."); + pw.println(" start [--user <USER_ID>] <PACKAGE | UID> <OP> "); + pw.println(" Starts a given operation for a particular application."); + pw.println(" stop [--user <USER_ID>] <PACKAGE | UID> <OP> "); + pw.println(" Stops a given operation for a particular application."); pw.println(" set [--user <USER_ID>] <PACKAGE | UID> <OP> <MODE>"); pw.println(" Set the mode for a particular application and operation."); pw.println(" get [--user <USER_ID>] <PACKAGE | UID> [<OP>]"); @@ -2347,6 +2357,34 @@ public class AppOpsService extends IAppOpsService.Stub { } return 0; } + case "start": { + int res = shell.parseUserPackageOp(true, err); + if (res < 0) { + return res; + } + + if (shell.packageName != null) { + shell.mInterface.startOperation(shell.mToken, + shell.op, shell.packageUid, shell.packageName, true); + } else { + return -1; + } + return 0; + } + case "stop": { + int res = shell.parseUserPackageOp(true, err); + if (res < 0) { + return res; + } + + if (shell.packageName != null) { + shell.mInterface.finishOperation(shell.mToken, + shell.op, shell.packageUid, shell.packageName); + } else { + return -1; + } + return 0; + } default: return shell.handleDefaultCommands(cmd); } diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java index 119c9df6c576..ea80ac1aecef 100644 --- a/services/core/java/com/android/server/IntentResolver.java +++ b/services/core/java/com/android/server/IntentResolver.java @@ -72,7 +72,7 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> { } } - private boolean filterEquals(IntentFilter f1, IntentFilter f2) { + public static boolean filterEquals(IntentFilter f1, IntentFilter f2) { int s1 = f1.countActions(); int s2 = f2.countActions(); if (s1 != s2) { diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java index 58731d27d97c..8e9e74ecedbf 100644 --- a/services/core/java/com/android/server/SystemService.java +++ b/services/core/java/com/android/server/SystemService.java @@ -16,6 +16,8 @@ package com.android.server; +import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT; + import android.app.ActivityThread; import android.content.Context; import android.os.IBinder; @@ -199,6 +201,9 @@ public abstract class SystemService { /** * Publish the service so it is accessible to other services and apps. + * + * @param name the name of the new service + * @param service the service object */ protected final void publishBinderService(String name, IBinder service) { publishBinderService(name, service, false); @@ -206,10 +211,29 @@ public abstract class SystemService { /** * Publish the service so it is accessible to other services and apps. + * + * @param name the name of the new service + * @param service the service object + * @param allowIsolated set to true to allow isolated sandboxed processes + * to access this service */ protected final void publishBinderService(String name, IBinder service, boolean allowIsolated) { - ServiceManager.addService(name, service, allowIsolated); + publishBinderService(name, service, allowIsolated, DUMP_FLAG_PRIORITY_DEFAULT); + } + + /** + * Publish the service so it is accessible to other services and apps. + * + * @param name the name of the new service + * @param service the service object + * @param allowIsolated set to true to allow isolated sandboxed processes + * to access this service + * @param dumpPriority supported dump priority levels as a bitmask + */ + protected final void publishBinderService(String name, IBinder service, + boolean allowIsolated, int dumpPriority) { + ServiceManager.addService(name, service, allowIsolated, dumpPriority); } /** diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java index 965714dc279a..26a8cf74d245 100644 --- a/services/core/java/com/android/server/TextServicesManagerService.java +++ b/services/core/java/com/android/server/TextServicesManagerService.java @@ -16,6 +16,9 @@ package com.android.server; +import static android.view.textservice.TextServicesManager.DISABLE_PER_PROFILE_SPELL_CHECKER; + +import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.inputmethod.InputMethodUtils; import com.android.internal.textservice.ISpellCheckerService; @@ -24,6 +27,7 @@ import com.android.internal.textservice.ISpellCheckerSession; import com.android.internal.textservice.ISpellCheckerSessionListener; import com.android.internal.textservice.ITextServicesManager; import com.android.internal.textservice.ITextServicesSessionListener; +import com.android.internal.textservice.LazyIntToIntMap; import com.android.internal.util.DumpUtils; import org.xmlpull.v1.XmlPullParserException; @@ -79,6 +83,10 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { private final UserManager mUserManager; private final Object mLock = new Object(); + @NonNull + @GuardedBy("mLock") + private final LazyIntToIntMap mSpellCheckerOwnerUserIdMap; + private static class TextServicesData { @UserIdInt private final int mUserId; @@ -294,6 +302,9 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { void onStopUser(@UserIdInt int userId) { synchronized (mLock) { + // Clear user ID mapping table. + mSpellCheckerOwnerUserIdMap.delete(userId); + // Clean per-user data TextServicesData tsd = mUserData.get(userId); if (tsd == null) return; @@ -313,12 +324,32 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { public TextServicesManagerService(Context context) { mContext = context; mUserManager = mContext.getSystemService(UserManager.class); + mSpellCheckerOwnerUserIdMap = new LazyIntToIntMap(callingUserId -> { + if (DISABLE_PER_PROFILE_SPELL_CHECKER) { + final long token = Binder.clearCallingIdentity(); + try { + final UserInfo parent = mUserManager.getProfileParent(callingUserId); + return (parent != null) ? parent.id : callingUserId; + } finally { + Binder.restoreCallingIdentity(token); + } + } else { + return callingUserId; + } + }); mMonitor = new TextServicesMonitor(); mMonitor.register(context, null, UserHandle.ALL, true); } private void initializeInternalStateLocked(@UserIdInt int userId) { + // When DISABLE_PER_PROFILE_SPELL_CHECKER is true, we make sure here that work profile users + // will never have non-null TextServicesData for their user ID. + if (DISABLE_PER_PROFILE_SPELL_CHECKER + && userId != mSpellCheckerOwnerUserIdMap.get(userId)) { + return; + } + TextServicesData tsd = mUserData.get(userId); if (tsd == null) { tsd = new TextServicesData(userId, mContext); @@ -470,7 +501,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { public SpellCheckerInfo getCurrentSpellChecker(String locale) { int userId = UserHandle.getCallingUserId(); synchronized (mLock) { - TextServicesData tsd = mUserData.get(userId); + final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); if (tsd == null) return null; return tsd.getCurrentSpellChecker(); @@ -488,7 +519,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { final int userId = UserHandle.getCallingUserId(); synchronized (mLock) { - TextServicesData tsd = mUserData.get(userId); + final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); if (tsd == null) return null; subtypeHashCode = @@ -569,7 +600,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { int callingUserId = UserHandle.getCallingUserId(); synchronized (mLock) { - TextServicesData tsd = mUserData.get(callingUserId); + final TextServicesData tsd = getDataFromCallingUserIdLocked(callingUserId); if (tsd == null) return; HashMap<String, SpellCheckerInfo> spellCheckerMap = tsd.mSpellCheckerMap; @@ -606,7 +637,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { int userId = UserHandle.getCallingUserId(); synchronized (mLock) { - TextServicesData tsd = mUserData.get(userId); + final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); if (tsd == null) return false; return tsd.isSpellCheckerEnabled(); @@ -643,7 +674,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { int callingUserId = UserHandle.getCallingUserId(); synchronized (mLock) { - TextServicesData tsd = mUserData.get(callingUserId); + final TextServicesData tsd = getDataFromCallingUserIdLocked(callingUserId); if (tsd == null) return null; ArrayList<SpellCheckerInfo> spellCheckerList = tsd.mSpellCheckerList; @@ -666,7 +697,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { int userId = UserHandle.getCallingUserId(); synchronized (mLock) { - TextServicesData tsd = mUserData.get(userId); + final TextServicesData tsd = getDataFromCallingUserIdLocked(userId); if (tsd == null) return; final ArrayList<SpellCheckerBindGroup> removeList = new ArrayList<>(); @@ -737,6 +768,36 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } } + /** + * @param callingUserId user ID of the calling process + * @return {@link TextServicesData} for the given user. {@code null} if spell checker is not + * temporarily / permanently available for the specified user + */ + @Nullable + private TextServicesData getDataFromCallingUserIdLocked(@UserIdInt int callingUserId) { + final int spellCheckerOwnerUserId = mSpellCheckerOwnerUserIdMap.get(callingUserId); + final TextServicesData data = mUserData.get(spellCheckerOwnerUserId); + if (DISABLE_PER_PROFILE_SPELL_CHECKER) { + if (spellCheckerOwnerUserId != callingUserId) { + // Calling process is running under child profile. + if (data == null) { + return null; + } + final SpellCheckerInfo info = data.getCurrentSpellChecker(); + if (info == null) { + return null; + } + final ServiceInfo serviceInfo = info.getServiceInfo(); + if ((serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { + // To be conservative, non pre-installed spell checker services are not allowed + // to be used for child profiles. + return null; + } + } + } + return data; + } + private static final class SessionRequest { public final int mUid; @Nullable diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 6726b6e1cc23..0de4f860d642 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -558,6 +558,9 @@ public class ActivityManagerService extends IActivityManager.Stub static final String SYSTEM_DEBUGGABLE = "ro.debuggable"; + // Maximum number of receivers an app can register. + private static final int MAX_RECEIVERS_ALLOWED_PER_APP = 1000; + // Amount of time after a call to stopAppSwitches() during which we will // prevent further untrusted switches from happening. static final long APP_SWITCH_DELAY_TIME = 5*1000; @@ -705,6 +708,8 @@ public class ActivityManagerService extends IActivityManager.Stub // Whether we should use SCHED_FIFO for UI and RenderThreads. private boolean mUseFifoUiScheduling = false; + private static final String SYSUI_COMPONENT_NAME = "com.android.systemui/.SystemUIService"; + BroadcastQueue mFgBroadcastQueue; BroadcastQueue mBgBroadcastQueue; // Convenient for easy iteration over the queues. Foreground is first @@ -804,6 +809,7 @@ public class ActivityManagerService extends IActivityManager.Stub boolean asProto) { if (asProto) return; doDump(fd, pw, new String[]{"activities"}, asProto); + doDump(fd, pw, new String[]{"service", SYSUI_COMPONENT_NAME}, asProto); } @Override @@ -20539,6 +20545,12 @@ public class ActivityManagerService extends IActivityManager.Stub rl = new ReceiverList(this, callerApp, callingPid, callingUid, userId, receiver); if (rl.app != null) { + final int totalReceiversForApp = rl.app.receivers.size(); + if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) { + throw new IllegalStateException("Too many receivers, total of " + + totalReceiversForApp + ", registered for pid: " + + rl.pid + ", callerPackage: " + callerPackage); + } rl.app.receivers.add(rl); } else { try { @@ -20567,11 +20579,18 @@ public class ActivityManagerService extends IActivityManager.Stub } BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission, callingUid, userId, instantApp, visibleToInstantApps); - rl.add(bf); - if (!bf.debugCheck()) { - Slog.w(TAG, "==> For Dynamic broadcast"); + if (rl.containsFilter(filter)) { + // STOPSHIP: To track if apps are doing this a lot for b/70677313. Change to Slog.w + Slog.wtf(TAG, "Receiver with filter " + filter + + " already registered for pid " + rl.pid + + ", callerPackage is " + callerPackage); + } else { + rl.add(bf); + if (!bf.debugCheck()) { + Slog.w(TAG, "==> For Dynamic broadcast"); + } + mReceiverResolver.addFilter(bf); } - mReceiverResolver.addFilter(bf); // Enqueue broadcasts for all existing stickies that match // this filter. diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index ae98ca02eba7..50cc0711c6da 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -1614,16 +1614,36 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo void setState(ActivityState state, String reason) { if (DEBUG_STATES) Slog.v(TAG_STATES, "State movement: " + this + " from:" + getState() + " to:" + state + " reason:" + reason); + final ActivityState prev = mState; + final boolean stateChanged = prev != state; + mState = state; - if (mState != prev) { + if (stateChanged) { if (mRecentTransitions.size() == MAX_STORED_STATE_TRANSITIONS) { mRecentTransitions.remove(0); } mRecentTransitions.add(new StateTransition(prev, state, reason)); } + + if (stateChanged && isState(DESTROYING, DESTROYED)) { + makeFinishingLocked(); + + // When moving to the destroyed state, immediately destroy the activity in the + // associated stack. Most paths for finishing an activity will handle an activity's path + // to destroy through mechanisms such as ActivityStackSupervisor#mFinishingActivities. + // However, moving to the destroyed state directly (as in the case of an app dying) and + // marking it as finished will lead to cleanup steps that will prevent later handling + // from happening. + if (isState(DESTROYED)) { + final ActivityStack stack = getStack(); + if (stack != null) { + stack.activityDestroyedLocked(this, reason); + } + } + } } ActivityState getState() { @@ -1712,8 +1732,12 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo mStackSupervisor.mStoppingActivities.remove(this); mStackSupervisor.mGoingToSleepActivities.remove(this); - // If the activity is stopped or stopping, cycle to the paused state. - if (isState(STOPPED, STOPPING)) { + // If the activity is stopped or stopping, cycle to the paused state. We avoid doing + // this when there is an activity waiting to become translucent as the extra binder + // calls will lead to noticeable jank. A later call to + // ActivityStack#ensureActivitiesVisibleLocked will bring the activity to the proper + // paused state. + if (isState(STOPPED, STOPPING) && stack.mTranslucentActivityWaiting == null) { // Capture reason before state change final String reason = getLifecycleDescription("makeVisibleIfNeeded"); @@ -1727,7 +1751,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } } catch (Exception e) { // Just skip on any failure; we'll make it visible when it next restarts. - Slog.w(TAG, "Exception thrown making visibile: " + intent.getComponent(), e); + Slog.w(TAG, "Exception thrown making visible: " + intent.getComponent(), e); } handleAlreadyVisible(); } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 728c07d02d46..9276abe68663 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -989,13 +989,32 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return; } + final ActivityDisplay display = getDisplay(); + + if (inSplitScreenSecondaryWindowingMode()) { + // If the stack is in split-screen seconardy mode, we need to make sure we move the + // primary split-screen stack forward in the case it is currently behind a fullscreen + // stack so both halves of the split-screen appear on-top and the fullscreen stack isn't + // cutting between them. + // TODO(b/70677280): This is a workaround until we can fix as part of b/70677280. + final ActivityStack topFullScreenStack = + display.getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN); + if (topFullScreenStack != null) { + final ActivityStack primarySplitScreenStack = display.getSplitScreenPrimaryStack(); + if (display.getIndexOf(topFullScreenStack) + > display.getIndexOf(primarySplitScreenStack)) { + primarySplitScreenStack.moveToFront(reason + " splitScreenToTop"); + } + } + } + if (!isActivityTypeHome() && returnsToHomeStack()) { // Make sure the home stack is behind this stack since that is where we should return to // when this stack is no longer visible. mStackSupervisor.moveHomeStackToFront(reason + " returnToHome"); } - getDisplay().positionChildAtTop(this); + display.positionChildAtTop(this); mStackSupervisor.setFocusStackUnchecked(reason, this); if (task != null) { insertTaskAtTop(task, null); @@ -3981,16 +4000,26 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai * state to destroy so only the cleanup here is needed. * * Note: Call before #removeActivityFromHistoryLocked. + * + * @param r The {@link ActivityRecord} to cleanup. + * @param cleanServices Whether services bound to the {@link ActivityRecord} should also be + * cleaned up. + * @param destroy Whether the {@link ActivityRecord} should be destroyed. + * @param clearProcess Whether the client process should be cleared. */ - private void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices, boolean setState) { + private void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices, boolean destroy, + boolean clearProcess) { onActivityRemovedFromStack(r); r.deferRelaunchUntilPaused = false; r.frozenBeforeDestroy = false; - if (setState) { + if (destroy) { if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (cleaning up)"); r.setState(DESTROYED, "cleanupActivityLocked"); + } + + if (clearProcess) { if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during cleanUp for activity " + r); r.app = null; } @@ -4209,7 +4238,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai boolean removedFromHistory = false; - cleanUpActivityLocked(r, false, false); + cleanUpActivityLocked(r, false /* cleanServices */, false /* destroy */, + false /*clearProcess*/); final boolean hadApp = r.app != null; @@ -4315,7 +4345,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (isInStackLocked(record) != null) { if (record.isState(DESTROYING, DESTROYED)) { - cleanUpActivityLocked(record, true, false); + cleanUpActivityLocked(record, true /* cleanServices */, false /* destroy */, + false /*clearProcess*/); removeActivityFromHistoryLocked(record, reason); } } @@ -4420,7 +4451,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai r.icicle = null; } } - cleanUpActivityLocked(r, true, true); + cleanUpActivityLocked(r, true /* cleanServices */, remove /* destroy */, + true /*clearProcess*/); if (remove) { removeActivityFromHistoryLocked(r, "appDied"); } diff --git a/services/core/java/com/android/server/am/ReceiverList.java b/services/core/java/com/android/server/am/ReceiverList.java index a98906313485..eee924f6b23f 100644 --- a/services/core/java/com/android/server/am/ReceiverList.java +++ b/services/core/java/com/android/server/am/ReceiverList.java @@ -17,11 +17,14 @@ package com.android.server.am; import android.content.IIntentReceiver; +import android.content.IntentFilter; import android.os.Binder; import android.os.IBinder; import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.proto.ProtoOutputStream; + +import com.android.server.IntentResolver; import com.android.server.am.proto.ReceiverListProto; import java.io.PrintWriter; @@ -67,6 +70,17 @@ final class ReceiverList extends ArrayList<BroadcastFilter> owner.unregisterReceiver(receiver); } + public boolean containsFilter(IntentFilter filter) { + final int N = size(); + for (int i = 0; i < N; i++) { + final BroadcastFilter f = get(i); + if (IntentResolver.filterEquals(f, filter)) { + return true; + } + } + return false; + } + void writeToProto(ProtoOutputStream proto, long fieldId) { long token = proto.start(fieldId); app.writeToProto(proto, ReceiverListProto.APP); diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 0564acb6e132..c6701d589663 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -92,6 +92,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.NoSuchElementException; /** * System implementation of MediaSessionManager @@ -139,6 +140,8 @@ public class MediaSessionService extends SystemService implements Monitor { // TODO(jaewan): Make it priority list for handling volume/media key. private final Map<SessionToken2, MediaController2> mSessionRecords = new ArrayMap<>(); + private final List<SessionTokensListenerRecord> mSessionTokensListeners = new ArrayList<>(); + public MediaSessionService(Context context) { super(context); mSessionManagerImpl = new SessionManagerImpl(); @@ -521,6 +524,7 @@ public class MediaSessionService extends SystemService implements Monitor { synchronized (mLock) { // List to keep the session services that need be removed because they don't exist // in the 'services' above. + boolean notifySessionTokensUpdated = false; Set<SessionToken2> sessionTokensToRemove = new HashSet<>(); for (SessionToken2 token : mSessionRecords.keySet()) { if (token.getType() != TYPE_SESSION) { @@ -553,11 +557,17 @@ public class MediaSessionService extends SystemService implements Monitor { // sessionTokensToRemove. if (!sessionTokensToRemove.remove(token)) { // New session service is found. - mSessionRecords.put(token, null); + notifySessionTokensUpdated |= addSessionRecordLocked(token); } } for (SessionToken2 token : sessionTokensToRemove) { mSessionRecords.remove(token); + notifySessionTokensUpdated |= removeSessionRecordLocked(token); + } + + if (notifySessionTokensUpdated) { + // TODO(jaewan): Pass proper user id to postSessionTokensUpdated(...) + postSessionTokensUpdated(UserHandle.USER_ALL); } } if (DEBUG) { @@ -780,10 +790,14 @@ public class MediaSessionService extends SystemService implements Monitor { void destroySession2Internal(SessionToken2 token) { synchronized (mLock) { + boolean notifySessionTokensUpdated = false; if (token.getType() == SessionToken2.TYPE_SESSION) { - mSessionRecords.remove(token); + notifySessionTokensUpdated |= removeSessionRecordLocked(token); } else { - mSessionRecords.put(token, null); + notifySessionTokensUpdated |= addSessionRecordLocked(token); + } + if (notifySessionTokensUpdated) { + postSessionTokensUpdated(UserHandle.getUserId(token.getUid())); } } } @@ -1517,8 +1531,11 @@ public class MediaSessionService extends SystemService implements Monitor { return false; } Context context = getContext(); - mSessionRecords.put(token, new MediaController2(context, token, - context.getMainExecutor(), new ControllerCallback(token))); + controller = new MediaController2(context, token, context.getMainExecutor(), + new ControllerCallback(token)); + if (addSessionRecordLocked(token, controller)) { + postSessionTokensUpdated(UserHandle.getUserId(token.getUid())); + } return true; } } @@ -1571,16 +1588,37 @@ public class MediaSessionService extends SystemService implements Monitor { } // TODO(jaewan): Protect this API with permission + // TODO(jaewan): "userId != calling user" needs extra protection @Override public void addSessionTokensListener(ISessionTokensListener listener, int userId, String packageName) { - // TODO(jaewan): Implement. + synchronized (mLock) { + final SessionTokensListenerRecord record = + new SessionTokensListenerRecord(listener, userId); + try { + listener.asBinder().linkToDeath(record, 0); + } catch (RemoteException e) { + } + mSessionTokensListeners.add(record); + } } // TODO(jaewan): Protect this API with permission @Override public void removeSessionTokensListener(ISessionTokensListener listener) { - // TODO(jaewan): Implement + synchronized (mLock) { + IBinder listenerBinder = listener.asBinder(); + for (SessionTokensListenerRecord record : mSessionTokensListeners) { + if (listenerBinder.equals(record.mListener.asBinder())) { + try { + listenerBinder.unlinkToDeath(record, 0); + } catch (NoSuchElementException e) { + } + mSessionTokensListeners.remove(record); + break; + } + } + } } private int verifySessionsRequest(ComponentName componentName, int userId, final int pid, @@ -1944,6 +1982,7 @@ public class MediaSessionService extends SystemService implements Monitor { final class MessageHandler extends Handler { private static final int MSG_SESSIONS_CHANGED = 1; private static final int MSG_VOLUME_INITIAL_DOWN = 2; + private static final int MSG_SESSIONS_TOKENS_CHANGED = 3; private final SparseArray<Integer> mIntegerCache = new SparseArray<>(); @Override @@ -1962,6 +2001,9 @@ public class MediaSessionService extends SystemService implements Monitor { } } break; + case MSG_SESSIONS_TOKENS_CHANGED: + pushSessionTokensChanged((int) msg.obj); + break; } } @@ -1990,4 +2032,74 @@ public class MediaSessionService extends SystemService implements Monitor { destroySession2Internal(mToken); } }; + + private final class SessionTokensListenerRecord implements IBinder.DeathRecipient { + private final ISessionTokensListener mListener; + private final int mUserId; + + public SessionTokensListenerRecord(ISessionTokensListener listener, int userId) { + mListener = listener; + mUserId = userId; // TODO should userId be mapped through mFullUserIds? + } + + @Override + public void binderDied() { + synchronized (mLock) { + mSessionTokensListeners.remove(this); + } + } + } + + private void postSessionTokensUpdated(int userId) { + mHandler.obtainMessage(MessageHandler.MSG_SESSIONS_TOKENS_CHANGED, userId).sendToTarget(); + } + + private void pushSessionTokensChanged(int userId) { + synchronized (mLock) { + List<Bundle> tokens = new ArrayList<>(); + for (SessionToken2 token : mSessionRecords.keySet()) { + // TODO(jaewan): Remove the check for UserHandle.USER_ALL (shouldn't happen). + // This happens when called form buildMediaSessionService2List(...). + if (UserHandle.getUserId(token.getUid()) == userId + || UserHandle.USER_ALL == userId) { + tokens.add(token.toBundle()); + } + } + + for (SessionTokensListenerRecord record : mSessionTokensListeners) { + // TODO should userId be mapped through mFullUserIds? + if (record.mUserId == userId || record.mUserId == UserHandle.USER_ALL) { + try { + record.mListener.onSessionTokensChanged(tokens); + } catch (RemoteException e) { + Log.w(TAG, "Failed to notify session tokens changed", e); + } + } + } + } + } + + private boolean addSessionRecordLocked(SessionToken2 token) { + return addSessionRecordLocked(token, null); + } + + private boolean addSessionRecordLocked(SessionToken2 token, MediaController2 controller) { + if (mSessionRecords.containsKey(token) && mSessionRecords.get(token) == controller) { + // The key/value pair already exists, no need to update. + return false; + } + + mSessionRecords.put(token, controller); + return true; + } + + private boolean removeSessionRecordLocked(SessionToken2 token) { + if (!mSessionRecords.containsKey(token)) { + // The key is already removed, no need to remove. + return false; + } + + mSessionRecords.remove(token); + return true; + } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index ef354cdee073..efc948bbcf1a 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -25,6 +25,8 @@ import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.content.pm.PackageManager.FEATURE_LEANBACK; import static android.content.pm.PackageManager.FEATURE_TELEVISION; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; +import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; import static android.os.UserHandle.USER_ALL; import static android.os.UserHandle.USER_NULL; import static android.os.UserHandle.USER_SYSTEM; @@ -65,6 +67,10 @@ import static android.service.notification.NotificationListenerService.TRIM_LIGH import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; +import static com.android.server.utils.PriorityDump.PRIORITY_ARG; +import static com.android.server.utils.PriorityDump.PRIORITY_ARG_CRITICAL; +import static com.android.server.utils.PriorityDump.PRIORITY_ARG_NORMAL; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; @@ -229,11 +235,12 @@ public class NotificationManagerService extends SystemService { static final float DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE = 5f; // message codes - static final int MESSAGE_TIMEOUT = 2; + static final int MESSAGE_DURATION_REACHED = 2; static final int MESSAGE_SAVE_POLICY_FILE = 3; static final int MESSAGE_SEND_RANKING_UPDATE = 4; static final int MESSAGE_LISTENER_HINTS_CHANGED = 5; static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6; + static final int MESSAGE_FINISH_TOKEN_TIMEOUT = 7; // ranking thread messages private static final int MESSAGE_RECONSIDER_RANKING = 1000; @@ -1476,7 +1483,8 @@ public class NotificationManagerService extends SystemService { IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED); getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter); - publishBinderService(Context.NOTIFICATION_SERVICE, mService); + publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false, + DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL); publishLocalService(NotificationManagerInternal.class, mInternalService); } @@ -1890,6 +1898,25 @@ public class NotificationManagerService extends SystemService { } @Override + public void finishToken(String pkg, ITransientNotification callback) { + synchronized (mToastQueue) { + long callingId = Binder.clearCallingIdentity(); + try { + int index = indexOfToastLocked(pkg, callback); + if (index >= 0) { + ToastRecord record = mToastQueue.get(index); + finishTokenLocked(record.token); + } else { + Slog.w(TAG, "Toast already killed. pkg=" + pkg + + " callback=" + callback); + } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + } + + @Override public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id, Notification notification, int userId) throws RemoteException { enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(), @@ -2896,6 +2923,8 @@ public class NotificationManagerService extends SystemService { dumpJson(pw, filter); } else if (filter.proto) { dumpProto(fd, filter); + } else if (filter.criticalPriority) { + dumpNotificationRecords(pw, filter); } else { dumpImpl(pw, filter); } @@ -3521,6 +3550,22 @@ public class NotificationManagerService extends SystemService { proto.flush(); } + private void dumpNotificationRecords(PrintWriter pw, @NonNull DumpFilter filter) { + synchronized (mNotificationLock) { + int N; + N = mNotificationList.size(); + if (N > 0) { + pw.println(" Notification List:"); + for (int i = 0; i < N; i++) { + final NotificationRecord nr = mNotificationList.get(i); + if (filter.filtered && !filter.matches(nr.sbn)) continue; + nr.dump(pw, " ", getContext(), filter.redact); + } + pw.println(" "); + } + } + } + void dumpImpl(PrintWriter pw, @NonNull DumpFilter filter) { pw.print("Current Notification Manager state"); if (filter.filtered) { @@ -3545,17 +3590,11 @@ public class NotificationManagerService extends SystemService { synchronized (mNotificationLock) { if (!zenOnly) { - N = mNotificationList.size(); - if (N > 0) { - pw.println(" Notification List:"); - for (int i=0; i<N; i++) { - final NotificationRecord nr = mNotificationList.get(i); - if (filter.filtered && !filter.matches(nr.sbn)) continue; - nr.dump(pw, " ", getContext(), filter.redact); - } - pw.println(" "); + // Priority filters are only set when called via bugreport. If set + // skip sections that are part of the critical section. + if (!filter.normalPriority) { + dumpNotificationRecords(pw, filter); } - if (!filter.filtered) { N = mLights.size(); if (N > 0) { @@ -4578,7 +4617,7 @@ public class NotificationManagerService extends SystemService { if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); try { record.callback.show(record.token); - scheduleTimeoutLocked(record); + scheduleDurationReachedLocked(record); return; } catch (RemoteException e) { Slog.w(TAG, "Object died trying to show notification " + record.callback @@ -4611,7 +4650,15 @@ public class NotificationManagerService extends SystemService { } ToastRecord lastToast = mToastQueue.remove(index); - mWindowManagerInternal.removeWindowToken(lastToast.token, true, DEFAULT_DISPLAY); + + mWindowManagerInternal.removeWindowToken(lastToast.token, false /* removeWindows */, + DEFAULT_DISPLAY); + // We passed 'false' for 'removeWindows' so that the client has time to stop + // rendering (as hide above is a one-way message), otherwise we could crash + // a client which was actively using a surface made from the token. However + // we need to schedule a timeout to make sure the token is eventually killed + // one way or another. + scheduleKillTokenTimeout(lastToast.token); keepProcessAliveIfNeededLocked(record.pid); if (mToastQueue.size() > 0) { @@ -4622,16 +4669,26 @@ public class NotificationManagerService extends SystemService { } } + void finishTokenLocked(IBinder t) { + mHandler.removeCallbacksAndMessages(t); + // We pass 'true' for 'removeWindows' to let the WindowManager destroy any + // remaining surfaces as either the client has called finishToken indicating + // it has successfully removed the views, or the client has timed out + // at which point anything goes. + mWindowManagerInternal.removeWindowToken(t, true /* removeWindows */, + DEFAULT_DISPLAY); + } + @GuardedBy("mToastQueue") - private void scheduleTimeoutLocked(ToastRecord r) + private void scheduleDurationReachedLocked(ToastRecord r) { mHandler.removeCallbacksAndMessages(r); - Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); + Message m = Message.obtain(mHandler, MESSAGE_DURATION_REACHED, r); long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; mHandler.sendMessageDelayed(m, delay); } - private void handleTimeout(ToastRecord record) + private void handleDurationReached(ToastRecord record) { if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); synchronized (mToastQueue) { @@ -4643,6 +4700,22 @@ public class NotificationManagerService extends SystemService { } @GuardedBy("mToastQueue") + private void scheduleKillTokenTimeout(IBinder token) + { + mHandler.removeCallbacksAndMessages(token); + Message m = Message.obtain(mHandler, MESSAGE_FINISH_TOKEN_TIMEOUT, token); + mHandler.sendMessageDelayed(m, 5); + } + + private void handleKillTokenTimeout(IBinder token) + { + if (DBG) Slog.d(TAG, "Kill Token Timeout token=" + token); + synchronized (mToastQueue) { + finishTokenLocked(token); + } + } + + @GuardedBy("mToastQueue") int indexOfToastLocked(String pkg, ITransientNotification callback) { IBinder cbak = callback.asBinder(); @@ -4839,8 +4912,11 @@ public class NotificationManagerService extends SystemService { { switch (msg.what) { - case MESSAGE_TIMEOUT: - handleTimeout((ToastRecord)msg.obj); + case MESSAGE_DURATION_REACHED: + handleDurationReached((ToastRecord)msg.obj); + break; + case MESSAGE_FINISH_TOKEN_TIMEOUT: + handleKillTokenTimeout((IBinder)msg.obj); break; case MESSAGE_SAVE_POLICY_FILE: handleSavePolicyFile(); @@ -6202,6 +6278,8 @@ public class NotificationManagerService extends SystemService { public boolean stats; public boolean redact = true; public boolean proto = false; + public boolean criticalPriority = false; + public boolean normalPriority = false; @NonNull public static DumpFilter parseFromArguments(String[] args) { @@ -6233,6 +6311,21 @@ public class NotificationManagerService extends SystemService { } else { filter.since = 0; } + } else if (PRIORITY_ARG.equals(a)) { + // Bugreport will call the service twice with priority arguments, first to dump + // critical sections and then non critical ones. Set approriate filters + // to generate the desired data. + if (ai < args.length - 1) { + ai++; + switch (args[ai]) { + case PRIORITY_ARG_CRITICAL: + filter.criticalPriority = true; + break; + case PRIORITY_ARG_NORMAL: + filter.normalPriority = true; + break; + } + } } } return filter; diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 0a870978d160..1a196982dc80 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -107,7 +107,7 @@ public class ZenModeHelper { @VisibleForTesting protected int mZenMode; private int mUser = UserHandle.USER_SYSTEM; @VisibleForTesting protected ZenModeConfig mConfig; - private AudioManagerInternal mAudioManager; + @VisibleForTesting protected AudioManagerInternal mAudioManager; protected PackageManager mPm; private long mSuppressedEffects; @@ -886,7 +886,8 @@ public class ZenModeHelper { exceptionPackages); } - private void applyZenToRingerMode() { + @VisibleForTesting + protected void applyZenToRingerMode() { if (mAudioManager == null) return; // force the ringer mode into compliance final int ringerModeInternal = mAudioManager.getRingerModeInternal(); @@ -901,15 +902,8 @@ public class ZenModeHelper { break; case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: if (ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(mConfig)) { - if (ringerModeInternal != AudioManager.RINGER_MODE_SILENT) { - setPreviousRingerModeSetting(ringerModeInternal); - newRingerModeInternal = AudioManager.RINGER_MODE_SILENT; - } - } else { - if (ringerModeInternal == AudioManager.RINGER_MODE_SILENT) { - newRingerModeInternal = getPreviousRingerModeSetting(); - setPreviousRingerModeSetting(null); - } + setPreviousRingerModeSetting(ringerModeInternal); + newRingerModeInternal = AudioManager.RINGER_MODE_SILENT; } break; case Global.ZEN_MODE_OFF: @@ -1003,7 +997,8 @@ public class ZenModeHelper { } } - private final class RingerModeDelegate implements AudioManagerInternal.RingerModeDelegate { + @VisibleForTesting + protected final class RingerModeDelegate implements AudioManagerInternal.RingerModeDelegate { @Override public String toString() { return TAG; @@ -1040,9 +1035,14 @@ public class ZenModeHelper { } break; } + if (newZen != -1) { setManualZenMode(newZen, null, "ringerModeInternal", null, false /*setRingerMode*/); + } else if (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS + && !ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(mConfig)) { + // in priority only with ringer not muted, save ringer mode changes + setPreviousRingerModeSetting(ringerModeNew); } if (isChange || newZen != -1 || ringerModeExternal != ringerModeExternalOut) { diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java index 0b9412b43bf8..863045c2aee7 100644 --- a/services/core/java/com/android/server/om/OverlayManagerSettings.java +++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java @@ -180,12 +180,14 @@ final class OverlayManagerSettings { List<OverlayInfo> getOverlaysForTarget(@NonNull final String targetPackageName, final int userId) { return selectWhereTarget(targetPackageName, userId) + .filter((i) -> !i.isStatic()) .map(SettingsItem::getOverlayInfo) .collect(Collectors.toList()); } ArrayMap<String, List<OverlayInfo>> getOverlaysForUser(final int userId) { return selectWhereUser(userId) + .filter((i) -> !i.isStatic()) .map(SettingsItem::getOverlayInfo) .collect(Collectors.groupingBy(info -> info.targetPackageName, ArrayMap::new, Collectors.toList())); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 9c692816aa6e..ee3261851e32 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -1613,6 +1613,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid); writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason); + writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions); + // Persist app icon if changed since last written File appIconFile = buildAppIconFile(sessionId, sessionsDir); if (params.appIcon == null && appIconFile.exists()) { @@ -1632,8 +1634,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { params.appIconLastModified = appIconFile.lastModified(); } - - writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions); } out.endTag(null, TAG_SESSION); @@ -1711,9 +1711,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI); params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE); params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID); - params.grantedRuntimePermissions = readGrantedRuntimePermissions(in); params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON); + params.grantedRuntimePermissions = readGrantedRuntimePermissions(in); + final File appIconFile = buildAppIconFile(sessionId, sessionsDir); if (appIconFile.exists()) { params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath()); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 98fb18a3883f..4f2d60e38111 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2981,6 +2981,9 @@ public class PackageManagerService extends IPackageManager.Stub } } } + // Adjust seInfo to ensure apps which share a sharedUserId are placed in the same + // SELinux domain. + setting.fixSeInfoLocked(); } // Now that we know all the packages we are keeping, @@ -10381,20 +10384,24 @@ public class PackageManagerService extends IPackageManager.Stub pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; } - // SELinux sandboxes become more restrictive as targetSdkVersion increases. - // To ensure that apps with sharedUserId are placed in the same selinux domain - // without breaking any assumptions about access, put them into the least - // restrictive targetSdkVersion=25 domain. - // TODO(b/72290969): Base this on the actual targetSdkVersion(s) of the apps within the - // sharedUserSetting, instead of defaulting to the least restrictive domain. - final int targetSdk = (sharedUserSetting != null) ? 25 - : pkg.applicationInfo.targetSdkVersion; + // Apps which share a sharedUserId must be placed in the same selinux domain. If this + // package is the first app installed as this shared user, set seInfoTargetSdkVersion to its + // targetSdkVersion. These are later adjusted in PackageManagerService's constructor to be + // the lowest targetSdkVersion of all apps within the shared user, which corresponds to the + // least restrictive selinux domain. + // NOTE: As new packages are installed / updated, the shared user's seinfoTargetSdkVersion + // will NOT be modified until next boot, even if a lower targetSdkVersion is used. This + // ensures that all packages continue to run in the same selinux domain. + final int targetSdkVersion = + ((sharedUserSetting != null) && (sharedUserSetting.packages.size() != 0)) ? + sharedUserSetting.seInfoTargetSdkVersion : pkg.applicationInfo.targetSdkVersion; // TODO(b/71593002): isPrivileged for sharedUser and appInfo should never be out of sync. // They currently can be if the sharedUser apps are signed with the platform key. final boolean isPrivileged = (sharedUserSetting != null) ? sharedUserSetting.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged(); - SELinuxMMAC.assignSeInfoValue(pkg, isPrivileged, targetSdk); + pkg.applicationInfo.seInfo = SELinuxMMAC.getSeInfo(pkg, isPrivileged, + pkg.applicationInfo.targetSandboxVersion, targetSdkVersion); pkg.mExtras = pkgSetting; pkg.applicationInfo.processName = fixProcessName( diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java index a9f15282133f..b47d96622e96 100644 --- a/services/core/java/com/android/server/pm/SELinuxMMAC.java +++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java @@ -64,6 +64,8 @@ public final class SELinuxMMAC { /** Required MAC permissions files */ private static List<File> sMacPermissions = new ArrayList<>(); + private static final String DEFAULT_SEINFO = "default"; + // Append privapp to existing seinfo label private static final String PRIVILEGED_APP_STR = ":privapp"; @@ -307,45 +309,56 @@ public final class SELinuxMMAC { } /** - * Applies a security label to a package based on an seinfo tag taken from a matched - * policy. All signature based policy stanzas are consulted and, if no match is - * found, the default seinfo label of 'default' (set in ApplicationInfo object) is - * used. The security label is attached to the ApplicationInfo instance of the package - * in the event that a matching policy was found. + * Selects a security label to a package based on input parameters and the seinfo tag taken + * from a matched policy. All signature based policy stanzas are consulted and, if no match + * is found, the default seinfo label of 'default' is used. The security label is attached to + * the ApplicationInfo instance of the package. * * @param pkg object representing the package to be labeled. + * @param isPrivileged boolean. + * @param targetSandboxVersion int. + * @param targetSdkVersion int. If this pkg runs as a sharedUser, targetSdkVersion is the + * greater of: lowest targetSdk for all pkgs in the sharedUser, or + * MINIMUM_TARGETSDKVERSION. + * @return String representing the resulting seinfo. */ - public static void assignSeInfoValue(PackageParser.Package pkg, boolean isPrivileged, - int targetSdkVersion) { + public static String getSeInfo(PackageParser.Package pkg, boolean isPrivileged, + int targetSandboxVersion, int targetSdkVersion) { + String seInfo = null; synchronized (sPolicies) { if (!sPolicyRead) { if (DEBUG_POLICY) { Slog.d(TAG, "Policy not read"); } - return; - } - for (Policy policy : sPolicies) { - String seInfo = policy.getMatchedSeInfo(pkg); - if (seInfo != null) { - pkg.applicationInfo.seInfo = seInfo; - break; + } else { + for (Policy policy : sPolicies) { + seInfo = policy.getMatchedSeInfo(pkg); + if (seInfo != null) { + break; + } } } } - if (pkg.applicationInfo.targetSandboxVersion == 2) - pkg.applicationInfo.seInfo += SANDBOX_V2_STR; + if (seInfo == null) { + seInfo = DEFAULT_SEINFO; + } + + if (targetSandboxVersion == 2) { + seInfo += SANDBOX_V2_STR; + } if (isPrivileged) { - pkg.applicationInfo.seInfo += PRIVILEGED_APP_STR; + seInfo += PRIVILEGED_APP_STR; } - pkg.applicationInfo.seInfo += TARGETSDKVERSION_STR + targetSdkVersion; + seInfo += TARGETSDKVERSION_STR + targetSdkVersion; if (DEBUG_POLICY_INSTALL) { Slog.i(TAG, "package (" + pkg.packageName + ") labeled with " + - "seinfo=" + pkg.applicationInfo.seInfo); + "seinfo=" + seInfo); } + return seInfo; } } diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java index 244613180d00..1d9afd9895b4 100644 --- a/services/core/java/com/android/server/pm/SharedUserSetting.java +++ b/services/core/java/com/android/server/pm/SharedUserSetting.java @@ -39,6 +39,10 @@ public final class SharedUserSetting extends SettingBase { int uidFlags; int uidPrivateFlags; + // The lowest targetSdkVersion of all apps in the sharedUserSetting, used to assign seinfo so + // that all apps within the sharedUser run in the same selinux context. + int seInfoTargetSdkVersion; + final ArraySet<PackageSetting> packages = new ArraySet<PackageSetting>(); final PackageSignatures signatures = new PackageSignatures(); @@ -84,6 +88,11 @@ public final class SharedUserSetting extends SettingBase { } void addPackage(PackageSetting packageSetting) { + // If this is the first package added to this shared user, temporarily (until next boot) use + // its targetSdkVersion when assigning seInfo for the shared user. + if ((packages.size() == 0) && (packageSetting.pkg != null)) { + seInfoTargetSdkVersion = packageSetting.pkg.applicationInfo.targetSdkVersion; + } if (packages.add(packageSetting)) { setFlags(this.pkgFlags | packageSetting.pkgFlags); setPrivateFlags(this.pkgPrivateFlags | packageSetting.pkgPrivateFlags); @@ -107,4 +116,26 @@ public final class SharedUserSetting extends SettingBase { public boolean isPrivileged() { return (this.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0; } + + /** + * Determine the targetSdkVersion for a sharedUser and update pkg.applicationInfo.seInfo + * to ensure that all apps within the sharedUser share an SELinux domain. Use the lowest + * targetSdkVersion of all apps within the shared user, which corresponds to the least + * restrictive selinux domain. + */ + public void fixSeInfoLocked() { + final List<PackageParser.Package> pkgList = getPackages(); + + for (PackageParser.Package pkg : pkgList) { + if (pkg.applicationInfo.targetSdkVersion < seInfoTargetSdkVersion) { + seInfoTargetSdkVersion = pkg.applicationInfo.targetSdkVersion; + } + } + for (PackageParser.Package pkg : pkgList) { + final boolean isPrivileged = isPrivileged() | pkg.isPrivileged(); + pkg.applicationInfo.seInfo = SELinuxMMAC.getSeInfo(pkg, isPrivileged, + pkg.applicationInfo.targetSandboxVersion, seInfoTargetSdkVersion); + } + } + } diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index 2ece2b280159..062a6b866c2b 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -203,8 +203,6 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":" + snapshotProfile, e); postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); - } finally { - IoUtils.closeQuietly(fd); } } @@ -283,6 +281,8 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { } catch (RemoteException e) { Slog.w(TAG, "Failed to call onSuccess after profile snapshot for " + packageName, e); + } finally { + IoUtils.closeQuietly(fd); } }); } diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java index 108b6b25b0c4..e1461356cc4c 100644 --- a/services/core/java/com/android/server/policy/GlobalActions.java +++ b/services/core/java/com/android/server/policy/GlobalActions.java @@ -20,33 +20,33 @@ import android.util.Slog; import com.android.server.LocalServices; import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs; -import com.android.server.statusbar.StatusBarManagerInternal; -import com.android.server.statusbar.StatusBarManagerInternal.GlobalActionsListener; +import com.android.server.policy.GlobalActionsProvider; -class GlobalActions implements GlobalActionsListener { +class GlobalActions implements GlobalActionsProvider.GlobalActionsListener { private static final String TAG = "GlobalActions"; private static final boolean DEBUG = false; private final Context mContext; - private final StatusBarManagerInternal mStatusBarInternal; + private final GlobalActionsProvider mGlobalActionsProvider; private final Handler mHandler; private final WindowManagerFuncs mWindowManagerFuncs; private LegacyGlobalActions mLegacyGlobalActions; private boolean mKeyguardShowing; private boolean mDeviceProvisioned; - private boolean mStatusBarConnected; + private boolean mGlobalActionsAvailable; private boolean mShowing; public GlobalActions(Context context, WindowManagerFuncs windowManagerFuncs) { mContext = context; mHandler = new Handler(); mWindowManagerFuncs = windowManagerFuncs; - mStatusBarInternal = LocalServices.getService(StatusBarManagerInternal.class); - // Some form factors do not have a status bar. - if (mStatusBarInternal != null) { - mStatusBarInternal.setGlobalActionsListener(this); + mGlobalActionsProvider = LocalServices.getService(GlobalActionsProvider.class); + if (mGlobalActionsProvider != null) { + mGlobalActionsProvider.setGlobalActionsListener(this); + } else { + Slog.i(TAG, "No GlobalActionsProvider found, defaulting to LegacyGlobalActions"); } } @@ -58,15 +58,15 @@ class GlobalActions implements GlobalActionsListener { public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) { if (DEBUG) Slog.d(TAG, "showDialog " + keyguardShowing + " " + deviceProvisioned); - if (mStatusBarInternal != null && mStatusBarInternal.isGlobalActionsDisabled()) { + if (mGlobalActionsProvider != null && mGlobalActionsProvider.isGlobalActionsDisabled()) { return; } mKeyguardShowing = keyguardShowing; mDeviceProvisioned = deviceProvisioned; mShowing = true; - if (mStatusBarConnected) { - mStatusBarInternal.showGlobalActions(); + if (mGlobalActionsAvailable) { mHandler.postDelayed(mShowTimeout, 5000); + mGlobalActionsProvider.showGlobalActions(); } else { // SysUI isn't alive, show legacy menu. ensureLegacyCreated(); @@ -88,11 +88,12 @@ class GlobalActions implements GlobalActionsListener { } @Override - public void onStatusBarConnectedChanged(boolean connected) { - if (DEBUG) Slog.d(TAG, "onStatusBarConnectedChanged " + connected); - mStatusBarConnected = connected; - if (mShowing && !mStatusBarConnected) { - // Status bar died but we need to be showing global actions still, show the legacy. + public void onGlobalActionsAvailableChanged(boolean available) { + if (DEBUG) Slog.d(TAG, "onGlobalActionsAvailableChanged " + available); + mGlobalActionsAvailable = available; + if (mShowing && !mGlobalActionsAvailable) { + // Global actions provider died but we need to be showing global actions still, show the + // legacy global acrions provider. ensureLegacyCreated(); mLegacyGlobalActions.showDialog(mKeyguardShowing, mDeviceProvisioned); } diff --git a/services/core/java/com/android/server/policy/GlobalActionsProvider.java b/services/core/java/com/android/server/policy/GlobalActionsProvider.java new file mode 100644 index 000000000000..d414314db2b5 --- /dev/null +++ b/services/core/java/com/android/server/policy/GlobalActionsProvider.java @@ -0,0 +1,46 @@ +/* + * 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.policy; + +/** Used with LocalServices to add custom handling to global actions. */ +public interface GlobalActionsProvider { + /** @return {@code true} if the dialog is enabled. */ + boolean isGlobalActionsDisabled(); + /** Set the listener that will handle various global actions evetns. */ + void setGlobalActionsListener(GlobalActionsListener listener); + /** Show the global actions UI to the user. */ + void showGlobalActions(); + + /** Listener to pass global actions events back to system. */ + public interface GlobalActionsListener { + /** + * Called when sysui starts and connects its status bar, or when the status bar binder + * dies indicating sysui is no longer alive. + */ + void onGlobalActionsAvailableChanged(boolean available); + + /** + * Callback from sysui to notify system that global actions has been successfully shown. + */ + void onGlobalActionsShown(); + + /** + * Callback from sysui to notify system that the user has dismissed global actions and + * it no longer needs to be displayed (even if sysui dies). + */ + void onGlobalActionsDismissed(); + } +} diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java index 96d062df0fea..9cb2441d5662 100644 --- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java +++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java @@ -17,18 +17,24 @@ package com.android.server.policy; import com.android.internal.app.AlertController; -import com.android.internal.app.AlertController.AlertParams; +import com.android.internal.globalactions.Action; +import com.android.internal.globalactions.ActionsAdapter; +import com.android.internal.globalactions.ActionsDialog; +import com.android.internal.globalactions.LongPressAction; +import com.android.internal.globalactions.SinglePressAction; +import com.android.internal.globalactions.ToggleAction; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.internal.util.EmergencyAffordanceManager; +import com.android.internal.R; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.TelephonyProperties; -import com.android.internal.R; +import com.android.internal.util.EmergencyAffordanceManager; import com.android.internal.widget.LockPatternUtils; +import com.android.server.policy.PowerAction; +import com.android.server.policy.RestartAction; import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs; import android.app.ActivityManager; -import android.app.Dialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; @@ -40,7 +46,6 @@ import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.net.ConnectivityManager; import android.os.Build; -import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.RemoteException; @@ -55,23 +60,14 @@ import android.service.dreams.IDreamManager; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.TelephonyManager; -import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; -import android.util.TypedValue; -import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.WindowManagerGlobal; -import android.view.accessibility.AccessibilityEvent; import android.widget.AdapterView; -import android.widget.BaseAdapter; -import android.widget.ImageView; -import android.widget.ImageView.ScaleType; -import android.widget.ListView; -import android.widget.TextView; import java.util.ArrayList; import java.util.List; @@ -107,12 +103,12 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn private final Runnable mOnDismiss; private ArrayList<Action> mItems; - private GlobalActionsDialog mDialog; + private ActionsDialog mDialog; private Action mSilentModeAction; private ToggleAction mAirplaneModeOn; - private MyAdapter mAdapter; + private ActionsAdapter mAdapter; private boolean mKeyguardShowing = false; private boolean mDeviceProvisioned = false; @@ -217,7 +213,7 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn * Create the global actions dialog. * @return A new dialog. */ - private GlobalActionsDialog createDialog() { + private ActionsDialog createDialog() { // Simple toggle style if there's no vibrator, otherwise use a tri-state if (!mHasVibrator) { mSilentModeAction = new SilentModeToggleAction(); @@ -232,7 +228,7 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn R.string.global_actions_airplane_mode_off_status) { @Override - void onToggle(boolean on) { + public void onToggle(boolean on) { if (mHasTelephony && Boolean.parseBoolean( SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) { mIsWaitingForEcmExit = true; @@ -282,7 +278,7 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn continue; } if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) { - mItems.add(new PowerAction()); + mItems.add(new PowerAction(mContext, mWindowManagerFuncs)); } else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) { mItems.add(mAirplaneModeOn); } else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) { @@ -307,7 +303,7 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn } else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) { mItems.add(getAssistAction()); } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) { - mItems.add(new RestartAction()); + mItems.add(new RestartAction(mContext, mWindowManagerFuncs)); } else { Log.e(TAG, "Invalid global action key " + actionKey); } @@ -319,14 +315,15 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn mItems.add(getEmergencyAction()); } - mAdapter = new MyAdapter(); + mAdapter = new ActionsAdapter(mContext, mItems, + () -> mDeviceProvisioned, () -> mKeyguardShowing); - AlertParams params = new AlertParams(mContext); + AlertController.AlertParams params = new AlertController.AlertParams(mContext); params.mAdapter = mAdapter; params.mOnClickListener = this; params.mForceInverseBackground = true; - GlobalActionsDialog dialog = new GlobalActionsDialog(mContext, params); + ActionsDialog dialog = new ActionsDialog(mContext, params); dialog.setCanceledOnTouchOutside(false); // Handled by the custom class. dialog.getListView().setItemsCanFocus(true); @@ -350,71 +347,6 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn return dialog; } - private final class PowerAction extends SinglePressAction implements LongPressAction { - private PowerAction() { - super(com.android.internal.R.drawable.ic_lock_power_off, - R.string.global_action_power_off); - } - - @Override - public boolean onLongPress() { - UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) { - mWindowManagerFuncs.rebootSafeMode(true); - return true; - } - return false; - } - - @Override - public boolean showDuringKeyguard() { - return true; - } - - @Override - public boolean showBeforeProvisioning() { - return true; - } - - @Override - public void onPress() { - // shutdown by making sure radio and power are handled accordingly. - mWindowManagerFuncs.shutdown(false /* confirm */); - } - } - - private final class RestartAction extends SinglePressAction implements LongPressAction { - private RestartAction() { - super(R.drawable.ic_restart, R.string.global_action_restart); - } - - @Override - public boolean onLongPress() { - UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) { - mWindowManagerFuncs.rebootSafeMode(true); - return true; - } - return false; - } - - @Override - public boolean showDuringKeyguard() { - return true; - } - - @Override - public boolean showBeforeProvisioning() { - return true; - } - - @Override - public void onPress() { - mWindowManagerFuncs.reboot(false /* confirm */); - } - } - - private class BugReportAction extends SinglePressAction implements LongPressAction { public BugReportAction() { @@ -693,330 +625,12 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn mAdapter.getItem(which).onPress(); } - /** - * The adapter used for the list within the global actions dialog, taking - * into account whether the keyguard is showing via - * {@link LegacyGlobalActions#mKeyguardShowing} and whether the device is provisioned - * via {@link LegacyGlobalActions#mDeviceProvisioned}. - */ - private class MyAdapter extends BaseAdapter { - - @Override - public int getCount() { - int count = 0; - - for (int i = 0; i < mItems.size(); i++) { - final Action action = mItems.get(i); - - if (mKeyguardShowing && !action.showDuringKeyguard()) { - continue; - } - if (!mDeviceProvisioned && !action.showBeforeProvisioning()) { - continue; - } - count++; - } - return count; - } - - @Override - public boolean isEnabled(int position) { - return getItem(position).isEnabled(); - } - - @Override - public boolean areAllItemsEnabled() { - return false; - } - - @Override - public Action getItem(int position) { - - int filteredPos = 0; - for (int i = 0; i < mItems.size(); i++) { - final Action action = mItems.get(i); - if (mKeyguardShowing && !action.showDuringKeyguard()) { - continue; - } - if (!mDeviceProvisioned && !action.showBeforeProvisioning()) { - continue; - } - if (filteredPos == position) { - return action; - } - filteredPos++; - } - - throw new IllegalArgumentException("position " + position - + " out of range of showable actions" - + ", filtered count=" + getCount() - + ", keyguardshowing=" + mKeyguardShowing - + ", provisioned=" + mDeviceProvisioned); - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - Action action = getItem(position); - return action.create(mContext, convertView, parent, LayoutInflater.from(mContext)); - } - } - // note: the scheme below made more sense when we were planning on having // 8 different things in the global actions dialog. seems overkill with // only 3 items now, but may as well keep this flexible approach so it will // be easy should someone decide at the last minute to include something // else, such as 'enable wifi', or 'enable bluetooth' - /** - * What each item in the global actions dialog must be able to support. - */ - private interface Action { - /** - * @return Text that will be announced when dialog is created. null - * for none. - */ - CharSequence getLabelForAccessibility(Context context); - - View create(Context context, View convertView, ViewGroup parent, LayoutInflater inflater); - - void onPress(); - - /** - * @return whether this action should appear in the dialog when the keygaurd - * is showing. - */ - boolean showDuringKeyguard(); - - /** - * @return whether this action should appear in the dialog before the - * device is provisioned. - */ - boolean showBeforeProvisioning(); - - boolean isEnabled(); - } - - /** - * An action that also supports long press. - */ - private interface LongPressAction extends Action { - boolean onLongPress(); - } - - /** - * A single press action maintains no state, just responds to a press - * and takes an action. - */ - private static abstract class SinglePressAction implements Action { - private final int mIconResId; - private final Drawable mIcon; - private final int mMessageResId; - private final CharSequence mMessage; - - protected SinglePressAction(int iconResId, int messageResId) { - mIconResId = iconResId; - mMessageResId = messageResId; - mMessage = null; - mIcon = null; - } - - protected SinglePressAction(int iconResId, Drawable icon, CharSequence message) { - mIconResId = iconResId; - mMessageResId = 0; - mMessage = message; - mIcon = icon; - } - - @Override - public boolean isEnabled() { - return true; - } - - public String getStatus() { - return null; - } - - @Override - abstract public void onPress(); - - @Override - public CharSequence getLabelForAccessibility(Context context) { - if (mMessage != null) { - return mMessage; - } else { - return context.getString(mMessageResId); - } - } - - @Override - public View create( - Context context, View convertView, ViewGroup parent, LayoutInflater inflater) { - View v = inflater.inflate(R.layout.global_actions_item, parent, false); - - ImageView icon = (ImageView) v.findViewById(R.id.icon); - TextView messageView = (TextView) v.findViewById(R.id.message); - - TextView statusView = (TextView) v.findViewById(R.id.status); - final String status = getStatus(); - if (!TextUtils.isEmpty(status)) { - statusView.setText(status); - } else { - statusView.setVisibility(View.GONE); - } - if (mIcon != null) { - icon.setImageDrawable(mIcon); - icon.setScaleType(ScaleType.CENTER_CROP); - } else if (mIconResId != 0) { - icon.setImageDrawable(context.getDrawable(mIconResId)); - } - if (mMessage != null) { - messageView.setText(mMessage); - } else { - messageView.setText(mMessageResId); - } - - return v; - } - } - - /** - * A toggle action knows whether it is on or off, and displays an icon - * and status message accordingly. - */ - private static abstract class ToggleAction implements Action { - - enum State { - Off(false), - TurningOn(true), - TurningOff(true), - On(false); - - private final boolean inTransition; - - State(boolean intermediate) { - inTransition = intermediate; - } - - public boolean inTransition() { - return inTransition; - } - } - - protected State mState = State.Off; - - // prefs - protected int mEnabledIconResId; - protected int mDisabledIconResid; - protected int mMessageResId; - protected int mEnabledStatusMessageResId; - protected int mDisabledStatusMessageResId; - - /** - * @param enabledIconResId The icon for when this action is on. - * @param disabledIconResid The icon for when this action is off. - * @param message The general information message, e.g 'Silent Mode' - * @param enabledStatusMessageResId The on status message, e.g 'sound disabled' - * @param disabledStatusMessageResId The off status message, e.g. 'sound enabled' - */ - public ToggleAction(int enabledIconResId, - int disabledIconResid, - int message, - int enabledStatusMessageResId, - int disabledStatusMessageResId) { - mEnabledIconResId = enabledIconResId; - mDisabledIconResid = disabledIconResid; - mMessageResId = message; - mEnabledStatusMessageResId = enabledStatusMessageResId; - mDisabledStatusMessageResId = disabledStatusMessageResId; - } - - /** - * Override to make changes to resource IDs just before creating the - * View. - */ - void willCreate() { - - } - - @Override - public CharSequence getLabelForAccessibility(Context context) { - return context.getString(mMessageResId); - } - - @Override - public View create(Context context, View convertView, ViewGroup parent, - LayoutInflater inflater) { - willCreate(); - - View v = inflater.inflate(R - .layout.global_actions_item, parent, false); - - ImageView icon = (ImageView) v.findViewById(R.id.icon); - TextView messageView = (TextView) v.findViewById(R.id.message); - TextView statusView = (TextView) v.findViewById(R.id.status); - final boolean enabled = isEnabled(); - - if (messageView != null) { - messageView.setText(mMessageResId); - messageView.setEnabled(enabled); - } - - boolean on = ((mState == State.On) || (mState == State.TurningOn)); - if (icon != null) { - icon.setImageDrawable(context.getDrawable( - (on ? mEnabledIconResId : mDisabledIconResid))); - icon.setEnabled(enabled); - } - - if (statusView != null) { - statusView.setText(on ? mEnabledStatusMessageResId : mDisabledStatusMessageResId); - statusView.setVisibility(View.VISIBLE); - statusView.setEnabled(enabled); - } - v.setEnabled(enabled); - - return v; - } - - @Override - public final void onPress() { - if (mState.inTransition()) { - Log.w(TAG, "shouldn't be able to toggle when in transition"); - return; - } - - final boolean nowOn = !(mState == State.On); - onToggle(nowOn); - changeStateFromPress(nowOn); - } - - @Override - public boolean isEnabled() { - return !mState.inTransition(); - } - - /** - * Implementations may override this if their state can be in on of the intermediate - * states until some notification is received (e.g airplane mode is 'turning off' until - * we know the wireless connections are back online - * @param buttonOn Whether the button was turned on or off - */ - protected void changeStateFromPress(boolean buttonOn) { - mState = buttonOn ? State.On : State.Off; - } - - abstract void onToggle(boolean on); - - public void updateState(State state) { - mState = state; - } - } - private class SilentModeToggleAction extends ToggleAction { public SilentModeToggleAction() { super(R.drawable.ic_audio_vol_mute, @@ -1027,7 +641,7 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn } @Override - void onToggle(boolean on) { + public void onToggle(boolean on) { if (on) { mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT); } else { @@ -1226,71 +840,4 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn mAirplaneState = on ? ToggleAction.State.On : ToggleAction.State.Off; } } - - private static final class GlobalActionsDialog extends Dialog implements DialogInterface { - private final Context mContext; - private final AlertController mAlert; - private final MyAdapter mAdapter; - - public GlobalActionsDialog(Context context, AlertParams params) { - super(context, getDialogTheme(context)); - mContext = getContext(); - mAlert = AlertController.create(mContext, this, getWindow()); - mAdapter = (MyAdapter) params.mAdapter; - params.apply(mAlert); - } - - private static int getDialogTheme(Context context) { - TypedValue outValue = new TypedValue(); - context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogTheme, - outValue, true); - return outValue.resourceId; - } - - @Override - protected void onStart() { - super.setCanceledOnTouchOutside(true); - super.onStart(); - } - - public ListView getListView() { - return mAlert.getListView(); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mAlert.installContent(); - } - - @Override - public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { - for (int i = 0; i < mAdapter.getCount(); ++i) { - CharSequence label = - mAdapter.getItem(i).getLabelForAccessibility(getContext()); - if (label != null) { - event.getText().add(label); - } - } - } - return super.dispatchPopulateAccessibilityEvent(event); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if (mAlert.onKeyDown(keyCode, event)) { - return true; - } - return super.onKeyDown(keyCode, event); - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (mAlert.onKeyUp(keyCode, event)) { - return true; - } - return super.onKeyUp(keyCode, event); - } - } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index eaa17ca104cb..ab0779b8fb33 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -541,6 +541,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { volatile boolean mGoingToSleep; volatile boolean mRequestedOrGoingToSleep; volatile boolean mRecentsVisible; + volatile boolean mNavBarVirtualKeyHapticFeedbackEnabled; volatile boolean mPictureInPictureVisible; // Written by vr manager thread, only read in this class. volatile private boolean mPersistentVrModeEnabled; @@ -4368,6 +4369,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override + public void setNavBarVirtualKeyHapticFeedbackEnabledLw(boolean enabled) { + mNavBarVirtualKeyHapticFeedbackEnabled = enabled; + } + + @Override public int adjustSystemUiVisibilityLw(int visibility) { mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility); @@ -5892,8 +5898,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { return result; } + // Enable haptics if down and virtual key without multiple repetitions. If this is a hard + // virtual key such as a navigation bar button, only vibrate if flag is enabled. + final boolean isNavBarVirtKey = ((event.getFlags() & KeyEvent.FLAG_VIRTUAL_HARD_KEY) != 0); boolean useHapticFeedback = down && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0 + && (!isNavBarVirtKey || mNavBarVirtualKeyHapticFeedbackEnabled) && event.getRepeatCount() == 0; // Handle special keys. diff --git a/services/core/java/com/android/server/policy/PowerAction.java b/services/core/java/com/android/server/policy/PowerAction.java new file mode 100644 index 000000000000..d2de58e96551 --- /dev/null +++ b/services/core/java/com/android/server/policy/PowerAction.java @@ -0,0 +1,61 @@ +/* + * 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.policy; + +import android.content.Context; +import android.os.UserManager; +import com.android.internal.globalactions.LongPressAction; +import com.android.internal.globalactions.SinglePressAction; +import com.android.internal.R; +import com.android.server.policy.WindowManagerPolicy; + +public final class PowerAction extends SinglePressAction implements LongPressAction { + private final Context mContext; + private final WindowManagerPolicy.WindowManagerFuncs mWindowManagerFuncs; + + public PowerAction(Context context, + WindowManagerPolicy.WindowManagerFuncs windowManagerFuncs) { + super(R.drawable.ic_lock_power_off, R.string.global_action_power_off); + mContext = context; + mWindowManagerFuncs = windowManagerFuncs; + } + + @Override + public boolean onLongPress() { + UserManager um = mContext.getSystemService(UserManager.class); + if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) { + mWindowManagerFuncs.rebootSafeMode(true); + return true; + } + return false; + } + + @Override + public boolean showDuringKeyguard() { + return true; + } + + @Override + public boolean showBeforeProvisioning() { + return true; + } + + @Override + public void onPress() { + // shutdown by making sure radio and power are handled accordingly. + mWindowManagerFuncs.shutdown(false /* confirm */); + } +} diff --git a/services/core/java/com/android/server/policy/RestartAction.java b/services/core/java/com/android/server/policy/RestartAction.java new file mode 100644 index 000000000000..0f13da82dad3 --- /dev/null +++ b/services/core/java/com/android/server/policy/RestartAction.java @@ -0,0 +1,60 @@ +/* + * 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.policy; + +import android.content.Context; +import android.os.UserManager; +import com.android.internal.globalactions.LongPressAction; +import com.android.internal.globalactions.SinglePressAction; +import com.android.internal.R; +import com.android.server.policy.WindowManagerPolicy; + +public final class RestartAction extends SinglePressAction implements LongPressAction { + private final Context mContext; + private final WindowManagerPolicy.WindowManagerFuncs mWindowManagerFuncs; + + public RestartAction(Context context, + WindowManagerPolicy.WindowManagerFuncs windowManagerFuncs) { + super(R.drawable.ic_restart, R.string.global_action_restart); + mContext = context; + mWindowManagerFuncs = windowManagerFuncs; + } + + @Override + public boolean onLongPress() { + UserManager um = mContext.getSystemService(UserManager.class); + if (!um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) { + mWindowManagerFuncs.rebootSafeMode(true); + return true; + } + return false; + } + + @Override + public boolean showDuringKeyguard() { + return true; + } + + @Override + public boolean showBeforeProvisioning() { + return true; + } + + @Override + public void onPress() { + mWindowManagerFuncs.reboot(false /* confirm */); + } +} diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index a07f5eb09ab7..ab331a5263c8 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -1531,6 +1531,11 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { void setPipVisibilityLw(boolean visible); /** + * Called by System UI to enable or disable haptic feedback on the navigation bar buttons. + */ + void setNavBarVirtualKeyHapticFeedbackEnabledLw(boolean enabled); + + /** * Specifies whether there is an on-screen navigation bar separate from the status bar. */ public boolean hasNavigationBar(); diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index 3ab771b7c6ec..095eaa5fde67 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -79,10 +79,6 @@ public interface StatusBarManagerInternal { void setCurrentUser(int newUserId); - boolean isGlobalActionsDisabled(); - void setGlobalActionsListener(GlobalActionsListener listener); - void showGlobalActions(); - /** * Set whether the top app currently hides the statusbar. * @@ -98,23 +94,4 @@ public interface StatusBarManagerInternal { * @param rotation rotation suggestion */ void onProposedRotationChanged(int rotation, boolean isValid); - - public interface GlobalActionsListener { - /** - * Called when sysui starts and connects its status bar, or when the status bar binder - * dies indicating sysui is no longer alive. - */ - void onStatusBarConnectedChanged(boolean connected); - - /** - * Callback from sysui to notify system that global actions has been successfully shown. - */ - void onGlobalActionsShown(); - - /** - * Callback from sysui to notify system that the user has dismissed global actions and - * it no longer needs to be displayed (even if sysui dies). - */ - void onGlobalActionsDismissed(); - } } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 343fb91eec53..59fce64b2f2c 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -47,8 +47,8 @@ import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.util.DumpUtils; import com.android.server.LocalServices; import com.android.server.notification.NotificationDelegate; +import com.android.server.policy.GlobalActionsProvider; import com.android.server.power.ShutdownThread; -import com.android.server.statusbar.StatusBarManagerInternal.GlobalActionsListener; import com.android.server.wm.WindowManagerService; import java.io.FileDescriptor; @@ -74,7 +74,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { // for disabling the status bar private final ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>(); - private GlobalActionsListener mGlobalActionListener; + private GlobalActionsProvider.GlobalActionsListener mGlobalActionListener; private IBinder mSysUiVisToken = new Binder(); private int mDisabled1 = 0; private int mDisabled2 = 0; @@ -162,6 +162,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { mWindowManager = windowManager; LocalServices.addService(StatusBarManagerInternal.class, mInternalService); + LocalServices.addService(GlobalActionsProvider.class, mGlobalActionsProvider); } /** @@ -375,26 +376,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } @Override - public boolean isGlobalActionsDisabled() { - return (mDisabled2 & DISABLE2_GLOBAL_ACTIONS) != 0; - } - - @Override - public void setGlobalActionsListener(GlobalActionsListener listener) { - mGlobalActionListener = listener; - mGlobalActionListener.onStatusBarConnectedChanged(mBar != null); - } - - @Override - public void showGlobalActions() { - if (mBar != null) { - try { - mBar.showGlobalActionsMenu(); - } catch (RemoteException ex) {} - } - } - - @Override public void setTopAppHidesStatusBar(boolean hidesStatusBar) { if (mBar != null) { try { @@ -427,6 +408,28 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } }; + private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() { + @Override + public boolean isGlobalActionsDisabled() { + return (mDisabled2 & DISABLE2_GLOBAL_ACTIONS) != 0; + } + + @Override + public void setGlobalActionsListener(GlobalActionsProvider.GlobalActionsListener listener) { + mGlobalActionListener = listener; + mGlobalActionListener.onGlobalActionsAvailableChanged(mBar != null); + } + + @Override + public void showGlobalActions() { + if (mBar != null) { + try { + mBar.showGlobalActionsMenu(); + } catch (RemoteException ex) {} + } + } + }; + // ================================================================================ // From IStatusBarService // ================================================================================ @@ -892,7 +895,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { private void notifyBarAttachChanged() { mHandler.post(() -> { if (mGlobalActionListener == null) return; - mGlobalActionListener.onStatusBarConnectedChanged(mBar != null); + mGlobalActionListener.onGlobalActionsAvailableChanged(mBar != null); }); } diff --git a/services/core/java/com/android/server/utils/PriorityDump.java b/services/core/java/com/android/server/utils/PriorityDump.java index fb92c2b8dd2f..d9fb9c07e993 100644 --- a/services/core/java/com/android/server/utils/PriorityDump.java +++ b/services/core/java/com/android/server/utils/PriorityDump.java @@ -94,11 +94,7 @@ public class SpringfieldNuclearPowerPlant extends Binder { * <p>To run the unit tests: * <pre><code> * - mmm -j32 frameworks/base/services/tests/servicestests/ && \ - adb install -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk && \ - adb shell am instrument -e class "com.android.server.utils.PriorityDumpTest" \ - -w "com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner" - + atest FrameworksServicesTests:PriorityDumpTest * </code></pre> * * @@ -108,6 +104,9 @@ public final class PriorityDump { public static final String PRIORITY_ARG = "--dump-priority"; public static final String PROTO_ARG = "--proto"; + public static final String PRIORITY_ARG_CRITICAL = "CRITICAL"; + public static final String PRIORITY_ARG_HIGH = "HIGH"; + public static final String PRIORITY_ARG_NORMAL = "NORMAL"; private PriorityDump() { throw new UnsupportedOperationException(); @@ -191,17 +190,19 @@ public final class PriorityDump { */ private static @PriorityType int getPriorityType(String arg) { switch (arg) { - case "CRITICAL": { + case PRIORITY_ARG_CRITICAL: { return PRIORITY_TYPE_CRITICAL; } - case "HIGH": { + case PRIORITY_ARG_HIGH: { return PRIORITY_TYPE_HIGH; } - case "NORMAL": { + case PRIORITY_ARG_NORMAL: { return PRIORITY_TYPE_NORMAL; } + default: { + return PRIORITY_TYPE_INVALID; + } } - return PRIORITY_TYPE_INVALID; } /** @@ -238,7 +239,7 @@ public final class PriorityDump { * Dumps all sections. * <p> * This method is called when - * {@link PriorityDump#dump(PriorityDumper, FileDescriptor, PrintWriter, String[], boolean)} + * {@link PriorityDump#dump(PriorityDumper, FileDescriptor, PrintWriter, String[])} * is called without priority arguments. By default, it calls the 3 {@code dumpTYPE} * methods, so sub-classes just need to implement the priority types they support. */ diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index c2cc7c983620..c04522e312b0 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -475,6 +475,20 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree setClientHidden(!visible); } + if (!mService.mClosingApps.contains(this) && !mService.mOpeningApps.contains(this)) { + // The token is not closing nor opening, so even if there is an animation set, that + // doesn't mean that it goes through the normal app transition cycle so we have + // to inform the docked controller about visibility change. + // TODO(multi-display): notify docked divider on all displays where visibility was + // affected. + mService.getDefaultDisplayContentLocked().getDockedDividerController() + .notifyAppVisibilityChanged(); + + // Take the screenshot before possibly hiding the WSA, otherwise the screenshot + // will not be taken. + mService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible); + } + // If we are hidden but there is no delay needed we immediately // apply the Surface transaction so that the ActivityManager // can have some guarantee on the Surface state following @@ -492,17 +506,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } SurfaceControl.closeTransaction(); } - - if (!mService.mClosingApps.contains(this) && !mService.mOpeningApps.contains(this)) { - // The token is not closing nor opening, so even if there is an animation set, that - // doesn't mean that it goes through the normal app transition cycle so we have - // to inform the docked controller about visibility change. - // TODO(multi-display): notify docked divider on all displays where visibility was - // affected. - mService.getDefaultDisplayContentLocked().getDockedDividerController() - .notifyAppVisibilityChanged(); - mService.mTaskSnapshotController.notifyAppVisibilityChanged(this, visible); - } } return delayed; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 0c6429a30db9..78c04e8e6a2c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -5922,6 +5922,18 @@ public class WindowManagerService extends IWindowManager.Stub } } + public void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Caller does not hold permission " + + android.Manifest.permission.STATUS_BAR); + } + + synchronized (mWindowMap) { + mPolicy.setNavBarVirtualKeyHapticFeedbackEnabledLw(enabled); + } + } + // TODO(multidisplay): StatusBar on multiple screens? private boolean updateStatusBarVisibilityLocked(int visibility) { if (mLastDispatchedSystemUiVisibility == visibility) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index ccfadc0eba90..625992633213 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -206,6 +206,8 @@ public final class SystemServer { "com.google.android.clockwork.lefty.WearLeftyService"; private static final String WEAR_TIME_SERVICE_CLASS = "com.google.android.clockwork.time.WearTimeService"; + private static final String WEAR_GLOBAL_ACTIONS_SERVICE_CLASS = + "com.android.clockwork.globalactions.GlobalActionsService"; private static final String ACCOUNT_SERVICE_CLASS = "com.android.server.accounts.AccountManagerService$Lifecycle"; private static final String CONTENT_SERVICE_CLASS = @@ -842,7 +844,8 @@ public final class SystemServer { !mFirstBoot, mOnlyCore, new PhoneWindowManager()); ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO); - ServiceManager.addService(Context.INPUT_SERVICE, inputManager); + ServiceManager.addService(Context.INPUT_SERVICE, inputManager, + /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL); traceEnd(); traceBeginAndSlog("SetWindowManagerService"); @@ -1546,6 +1549,10 @@ public final class SystemServer { mSystemServiceManager.startService(WEAR_LEFTY_SERVICE_CLASS); traceEnd(); } + + traceBeginAndSlog("StartWearGlobalActionsService"); + mSystemServiceManager.startService(WEAR_GLOBAL_ACTIONS_SERVICE_CLASS); + traceEnd(); } if (!disableSlices) { diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java b/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java index 5e3c9741ae70..3d2d8afd4a35 100644 --- a/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java +++ b/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java @@ -20,6 +20,7 @@ import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPOR import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.argThat; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -30,6 +31,7 @@ import android.content.ServiceConnection; import android.os.Bundle; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; + import com.android.server.testing.FrameworkRobolectricTestRunner; import com.android.server.testing.SystemLoaderPackages; import org.junit.Before; @@ -45,7 +47,6 @@ import org.robolectric.annotation.Config; @SystemLoaderPackages({"com.android.server.backup"}) @Presubmit public class TransportClientManagerTest { - private static final String PACKAGE_NAME = "random.package.name"; private static final String CLASS_NAME = "random.package.name.transport.Transport"; @@ -72,15 +73,30 @@ public class TransportClientManagerTest { } @Test + public void testGetTransportClient() { + TransportClient transportClient = + mTransportClientManager.getTransportClient(mTransportComponent, "caller"); + + // Connect to be able to extract the intent + transportClient.connectAsync(mTransportConnectionListener, "caller"); + verify(mContext) + .bindServiceAsUser( + argThat(matchesIntentAndExtras(mBindIntent)), + any(ServiceConnection.class), + anyInt(), + any(UserHandle.class)); + } + + @Test public void testGetTransportClient_withExtras_createsTransportClientWithCorrectIntent() { Bundle extras = new Bundle(); extras.putBoolean("random_extra", true); - mBindIntent.putExtras(extras); TransportClient transportClient = mTransportClientManager.getTransportClient(mTransportComponent, extras, "caller"); transportClient.connectAsync(mTransportConnectionListener, "caller"); + mBindIntent.putExtras(extras); verify(mContext) .bindServiceAsUser( argThat(matchesIntentAndExtras(mBindIntent)), @@ -89,6 +105,17 @@ public class TransportClientManagerTest { any(UserHandle.class)); } + @Test + public void testDisposeOfTransportClient() { + TransportClient transportClient = + spy(mTransportClientManager.getTransportClient(mTransportComponent, "caller")); + + mTransportClientManager.disposeOfTransportClient(transportClient, "caller"); + + verify(transportClient).unbind(any()); + verify(transportClient).markAsDisposed(); + } + private ArgumentMatcher<Intent> matchesIntentAndExtras(Intent expectedIntent) { return (Intent actualIntent) -> { if (!expectedIntent.filterEquals(actualIntent)) { diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java index ff1644cb75ad..5b65473e0783 100644 --- a/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java +++ b/services/robotests/src/com/android/server/backup/transport/TransportClientTest.java @@ -26,16 +26,21 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; +import static org.robolectric.shadow.api.Shadow.extract; import static org.testng.Assert.expectThrows; +import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Handler; +import android.os.HandlerThread; import android.os.Looper; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; @@ -43,10 +48,9 @@ import android.util.Log; import com.android.internal.backup.IBackupTransport; import com.android.server.EventLogTags; -import com.android.server.backup.TransportManager; import com.android.server.testing.FrameworkRobolectricTestRunner; -import com.android.server.testing.SystemLoaderClasses; import com.android.server.testing.SystemLoaderPackages; +import com.android.server.testing.shadows.FrameworkShadowLooper; import com.android.server.testing.shadows.ShadowCloseGuard; import com.android.server.testing.shadows.ShadowEventLog; import com.android.server.testing.shadows.ShadowSlog; @@ -61,11 +65,19 @@ import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowLog; import org.robolectric.shadows.ShadowLooper; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + @RunWith(FrameworkRobolectricTestRunner.class) @Config( manifest = Config.NONE, sdk = 26, - shadows = {ShadowEventLog.class, ShadowCloseGuard.class, ShadowSlog.class} + shadows = { + ShadowEventLog.class, + ShadowCloseGuard.class, + ShadowSlog.class, + FrameworkShadowLooper.class + } ) @SystemLoaderPackages({"com.android.server.backup"}) @Presubmit @@ -80,14 +92,16 @@ public class TransportClientTest { private ComponentName mTransportComponent; private String mTransportString; private Intent mBindIntent; - private ShadowLooper mShadowLooper; + private FrameworkShadowLooper mShadowMainLooper; + private ShadowLooper mShadowWorkerLooper; + private Handler mWorkerHandler; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); Looper mainLooper = Looper.getMainLooper(); - mShadowLooper = shadowOf(mainLooper); + mShadowMainLooper = extract(mainLooper); mTransportComponent = new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".transport.Transport"); mTransportString = mTransportComponent.flattenToShortString(); @@ -107,6 +121,11 @@ public class TransportClientTest { anyInt(), any(UserHandle.class))) .thenReturn(true); + + HandlerThread workerThread = new HandlerThread("worker"); + workerThread.start(); + mShadowWorkerLooper = shadowOf(workerThread.getLooper()); + mWorkerHandler = workerThread.getThreadHandler(); } @Test @@ -129,12 +148,11 @@ public class TransportClientTest { @Test public void testConnectAsync_callsListenerWhenConnected() throws Exception { mTransportClient.connectAsync(mTransportConnectionListener, "caller"); - - // Simulate framework connecting ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext); + connection.onServiceConnected(mTransportComponent, mTransportBinder); - mShadowLooper.runToEndOfTasks(); + mShadowMainLooper.runToEndOfTasks(); verify(mTransportConnectionListener) .onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportClient)); } @@ -148,8 +166,7 @@ public class TransportClientTest { mTransportClient.connectAsync(mTransportConnectionListener2, "caller2"); connection.onServiceConnected(mTransportComponent, mTransportBinder); - - mShadowLooper.runToEndOfTasks(); + mShadowMainLooper.runToEndOfTasks(); verify(mTransportConnectionListener) .onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportClient)); verify(mTransportConnectionListener2) @@ -164,7 +181,7 @@ public class TransportClientTest { mTransportClient.connectAsync(mTransportConnectionListener2, "caller2"); - mShadowLooper.runToEndOfTasks(); + mShadowMainLooper.runToEndOfTasks(); verify(mTransportConnectionListener2) .onTransportConnectionResult(any(IBackupTransport.class), eq(mTransportClient)); } @@ -180,7 +197,7 @@ public class TransportClientTest { mTransportClient.connectAsync(mTransportConnectionListener, "caller"); - mShadowLooper.runToEndOfTasks(); + mShadowMainLooper.runToEndOfTasks(); verify(mTransportConnectionListener) .onTransportConnectionResult(isNull(), eq(mTransportClient)); } @@ -233,11 +250,11 @@ public class TransportClientTest { @Test public void testConnectAsync_callsListenerIfBindingDies() throws Exception { mTransportClient.connectAsync(mTransportConnectionListener, "caller"); - ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext); + connection.onBindingDied(mTransportComponent); - mShadowLooper.runToEndOfTasks(); + mShadowMainLooper.runToEndOfTasks(); verify(mTransportConnectionListener) .onTransportConnectionResult(isNull(), eq(mTransportClient)); } @@ -251,8 +268,7 @@ public class TransportClientTest { mTransportClient.connectAsync(mTransportConnectionListener2, "caller2"); connection.onBindingDied(mTransportComponent); - - mShadowLooper.runToEndOfTasks(); + mShadowMainLooper.runToEndOfTasks(); verify(mTransportConnectionListener) .onTransportConnectionResult(isNull(), eq(mTransportClient)); verify(mTransportConnectionListener2) @@ -271,9 +287,9 @@ public class TransportClientTest { @Test public void testConnectAsync_afterOnServiceConnected_logsBoundAndConnectedTransitions() { ShadowEventLog.setUp(); - mTransportClient.connectAsync(mTransportConnectionListener, "caller1"); ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext); + connection.onServiceConnected(mTransportComponent, mTransportBinder); assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1); @@ -283,9 +299,9 @@ public class TransportClientTest { @Test public void testConnectAsync_afterOnBindingDied_logsBoundAndUnboundTransitions() { ShadowEventLog.setUp(); - mTransportClient.connectAsync(mTransportConnectionListener, "caller1"); ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext); + connection.onBindingDied(mTransportComponent); assertEventLogged(EventLogTags.BACKUP_TRANSPORT_LIFECYCLE, mTransportString, 1); @@ -293,6 +309,66 @@ public class TransportClientTest { } @Test + public void testConnect_whenConnected_returnsTransport() throws Exception { + mTransportClient.connectAsync(mTransportConnectionListener, "caller1"); + ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext); + connection.onServiceConnected(mTransportComponent, mTransportBinder); + + IBackupTransport transportBinder = + runInWorkerThread(() -> mTransportClient.connect("caller2")); + + assertThat(transportBinder).isNotNull(); + } + + @Test + public void testConnect_afterOnServiceDisconnected_returnsNull() throws Exception { + mTransportClient.connectAsync(mTransportConnectionListener, "caller1"); + ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext); + connection.onServiceConnected(mTransportComponent, mTransportBinder); + connection.onServiceDisconnected(mTransportComponent); + + IBackupTransport transportBinder = + runInWorkerThread(() -> mTransportClient.connect("caller2")); + + assertThat(transportBinder).isNull(); + } + + @Test + public void testConnect_afterOnBindingDied_returnsNull() throws Exception { + mTransportClient.connectAsync(mTransportConnectionListener, "caller1"); + ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext); + connection.onBindingDied(mTransportComponent); + + IBackupTransport transportBinder = + runInWorkerThread(() -> mTransportClient.connect("caller2")); + + assertThat(transportBinder).isNull(); + } + + @Test + public void testConnect_callsThroughToConnectAsync() throws Exception { + // We can't mock bindServiceAsUser() instead of connectAsync() and call the listener inline + // because in our code in TransportClient we assume this is NOT run inline, such that the + // reentrant lock can't be acquired by the listener at the call-site of bindServiceAsUser(), + // which is what would happened if we mocked bindServiceAsUser() to call the listener + // inline. + TransportClient transportClient = spy(mTransportClient); + doAnswer( + invocation -> { + TransportConnectionListener listener = invocation.getArgument(0); + listener.onTransportConnectionResult(mTransportBinder, transportClient); + return null; + }) + .when(transportClient) + .connectAsync(any(), any()); + + IBackupTransport transportBinder = + runInWorkerThread(() -> transportClient.connect("caller")); + + assertThat(transportBinder).isNotNull(); + } + + @Test public void testUnbind_whenConnected_logsDisconnectedAndUnboundTransitions() { mTransportClient.connectAsync(mTransportConnectionListener, "caller1"); ServiceConnection connection = verifyBindServiceAsUserAndCaptureServiceConnection(mContext); @@ -447,6 +523,19 @@ public class TransportClientTest { assertThat(ShadowCloseGuard.hasReported()).isFalse(); } + @Nullable + private <T> T runInWorkerThread(Supplier<T> supplier) throws Exception { + CompletableFuture<T> future = new CompletableFuture<>(); + mWorkerHandler.post(() -> future.complete(supplier.get())); + // Although we are using a separate looper, we are still calling runToEndOfTasks() in the + // main thread (Robolectric only *simulates* multi-thread). The only option left is to fool + // the caller. + mShadowMainLooper.setCurrentThread(false); + mShadowWorkerLooper.runToEndOfTasks(); + mShadowMainLooper.reset(); + return future.get(); + } + private void assertEventLogged(int tag, Object... values) { assertThat(ShadowEventLog.hasEvent(tag, values)).isTrue(); } diff --git a/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java new file mode 100644 index 000000000000..c0eeb3890a86 --- /dev/null +++ b/services/robotests/src/com/android/server/testing/shadows/FrameworkShadowLooper.java @@ -0,0 +1,48 @@ +/* + * 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.testing.shadows; + +import android.os.Looper; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.annotation.RealObject; +import org.robolectric.shadows.ShadowLooper; + +import java.util.Optional; + +@Implements(value = Looper.class, inheritImplementationMethods = true) +public class FrameworkShadowLooper extends ShadowLooper { + @RealObject private Looper mLooper; + private Optional<Boolean> mIsCurrentThread = Optional.empty(); + + public void setCurrentThread(boolean currentThread) { + mIsCurrentThread = Optional.of(currentThread); + } + + public void reset() { + mIsCurrentThread = Optional.empty(); + } + + @Implementation + public boolean isCurrentThread() { + if (mIsCurrentThread.isPresent()) { + return mIsCurrentThread.get(); + } + return Thread.currentThread() == mLooper.getThread(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java index a527e17b393b..fad7180f15a9 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java @@ -25,6 +25,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static com.android.server.am.ActivityStack.ActivityState.DESTROYED; import static com.android.server.am.ActivityStack.ActivityState.DESTROYING; import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING; +import static com.android.server.am.ActivityStack.ActivityState.PAUSED; import static com.android.server.am.ActivityStack.ActivityState.PAUSING; import static com.android.server.am.ActivityStack.ActivityState.STOPPED; import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING; @@ -135,6 +136,16 @@ public class ActivityRecordTests extends ActivityTestsBase { mActivity.makeVisibleIfNeeded(null /* starting */); assertTrue(mActivity.isState(INITIALIZING)); + + // Make sure the state does not change if we are not the current top activity. + mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind"); + + // Make sure that the state does not change when we have an activity becoming translucent + final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build(); + mStack.mTranslucentActivityWaiting = topActivity; + mActivity.makeVisibleIfNeeded(null /* starting */); + + assertTrue(mActivity.isState(STOPPED)); } @Test @@ -202,4 +213,20 @@ public class ActivityRecordTests extends ActivityTestsBase { verify(mService.mStackSupervisor, times(1)).canPlaceEntityOnDisplay(anyInt(), eq(expected), anyInt(), anyInt(), eq(record.info)); } + + @Test + public void testFinishingAfterDestroying() throws Exception { + assertFalse(mActivity.finishing); + mActivity.setState(DESTROYING, "testFinishingAfterDestroying"); + assertTrue(mActivity.isState(DESTROYING)); + assertTrue(mActivity.finishing); + } + + @Test + public void testFinishingAfterDestroyed() throws Exception { + assertFalse(mActivity.finishing); + mActivity.setState(DESTROYED, "testFinishingAfterDestroyed"); + assertTrue(mActivity.isState(DESTROYED)); + assertTrue(mActivity.finishing); + } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java index c62820e8e3c3..a98d5a161f80 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java @@ -47,7 +47,7 @@ import org.junit.Test; * Tests for the {@link ActivityStack} class. * * Build/Install/Run: - * atest ActivityStackTests + * atest FrameworksServicesTests:com.android.server.am.ActivityStackTests */ @SmallTest @Presubmit @@ -425,6 +425,31 @@ public class ActivityStackTests extends ActivityTestsBase { assertTrue(display.getStackAboveHome() == fullscreenStack2); } + @Test + public void testSplitScreenMoveToFront() throws Exception { + final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay(); + final TestActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(display, + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final TestActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(display, + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(display, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */); + + splitScreenPrimary.setIsTranslucent(false); + splitScreenSecondary.setIsTranslucent(false); + assistantStack.setIsTranslucent(false); + + assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */)); + assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */)); + assertTrue(assistantStack.shouldBeVisible(null /* starting */)); + + splitScreenSecondary.moveToFront("testSplitScreenMoveToFront"); + + assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */)); + assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */)); + assertFalse(assistantStack.shouldBeVisible(null /* starting */)); + } + private <T extends ActivityStack> T createStackForShouldBeVisibleTest( ActivityDisplay display, int windowingMode, int activityType, boolean onTop) { final T stack = display.createStack(windowingMode, activityType, onTop); diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java index 206ee7af2966..34c5db3c220b 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -637,6 +637,10 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override + public void setNavBarVirtualKeyHapticFeedbackEnabledLw(boolean enabled) { + } + + @Override public void onSystemUiStarted() { } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 6144c516750d..6948b722049b 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -26,6 +26,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -37,7 +38,12 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.media.AudioAttributes; +import android.media.AudioManager; +import android.media.AudioManagerInternal; +import android.media.VolumePolicy; import android.provider.Settings; +import android.provider.Settings.Global; +import android.service.notification.ZenModeConfig; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -241,4 +247,119 @@ public class ZenModeHelperTest extends UiServiceTestCase { verify(mNotificationManager, never()).notify(eq(ZenModeHelper.TAG), eq(SystemMessage.NOTE_ZEN_UPGRADE), any()); } + + @Test + public void testZenSetInternalRinger_AllPriorityNotificationSoundsMuted() { + AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class); + mZenModeHelperSpy.mAudioManager = mAudioManager; + Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_RINGER_LEVEL, + Integer.toString(AudioManager.RINGER_MODE_NORMAL)); + + // 1. Current ringer is normal + when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); + // Set zen to priority-only with all notification sounds muted (so ringer will be muted) + mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; + mZenModeHelperSpy.mConfig.allowReminders = false; + mZenModeHelperSpy.mConfig.allowCalls = false; + mZenModeHelperSpy.mConfig.allowMessages = false; + mZenModeHelperSpy.mConfig.allowEvents = false; + mZenModeHelperSpy.mConfig.allowRepeatCallers= false; + + // 2. apply priority only zen - verify ringer is set to silent + mZenModeHelperSpy.applyZenToRingerMode(); + verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT, + mZenModeHelperSpy.TAG); + + // 3. apply zen off - verify zen is set to prevoius ringer (normal) + when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT); + mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_OFF; + mZenModeHelperSpy.applyZenToRingerMode(); + verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL, + mZenModeHelperSpy.TAG); + } + + @Test + public void testZenSetInternalRinger_NotAllPriorityNotificationSoundsMuted_StartNormal() { + AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class); + mZenModeHelperSpy.mAudioManager = mAudioManager; + Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_RINGER_LEVEL, + Integer.toString(AudioManager.RINGER_MODE_NORMAL)); + + // 1. Current ringer is normal + when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); + mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; + mZenModeHelperSpy.mConfig.allowReminders = true; + + // 2. apply priority only zen - verify ringer is normal + mZenModeHelperSpy.applyZenToRingerMode(); + verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL, + mZenModeHelperSpy.TAG); + + // 3. apply zen off - verify ringer remains normal + when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); + mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_OFF; + mZenModeHelperSpy.applyZenToRingerMode(); + verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL, + mZenModeHelperSpy.TAG); + } + + @Test + public void testZenSetInternalRinger_NotAllPriorityNotificationSoundsMuted_StartSilent() { + AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class); + mZenModeHelperSpy.mAudioManager = mAudioManager; + Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_RINGER_LEVEL, + Integer.toString(AudioManager.RINGER_MODE_SILENT)); + + // 1. Current ringer is silent + when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT); + mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; + mZenModeHelperSpy.mConfig.allowReminders = true; + + // 2. apply priority only zen - verify ringer is silent + mZenModeHelperSpy.applyZenToRingerMode(); + verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT, + mZenModeHelperSpy.TAG); + + // 3. apply zen-off - verify ringer is still silent + when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT); + mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_OFF; + mZenModeHelperSpy.applyZenToRingerMode(); + verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT, + mZenModeHelperSpy.TAG); + } + + @Test + public void testZenSetInternalRinger_NotAllPriorityNotificationSoundsMuted_RingerChanges() { + AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class); + mZenModeHelperSpy.mAudioManager = mAudioManager; + Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_RINGER_LEVEL, + Integer.toString(AudioManager.RINGER_MODE_NORMAL)); + + // 1. Current ringer is normal + when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); + // Set zen to priority-only with all notification sounds muted (so ringer will be muted) + mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; + mZenModeHelperSpy.mConfig.allowReminders = true; + + // 2. apply priority only zen - verify zen will still be normal + mZenModeHelperSpy.applyZenToRingerMode(); + verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL, + mZenModeHelperSpy.TAG); + + // 3. change ringer from normal to silent, verify previous ringer set to new rigner (silent) + ZenModeHelper.RingerModeDelegate ringerModeDelegate = + mZenModeHelperSpy.new RingerModeDelegate(); + ringerModeDelegate.onSetRingerModeInternal(AudioManager.RINGER_MODE_NORMAL, + AudioManager.RINGER_MODE_SILENT, "test", AudioManager.RINGER_MODE_NORMAL, + VolumePolicy.DEFAULT); + assertEquals(AudioManager.RINGER_MODE_SILENT, Global.getInt(mContext.getContentResolver(), + Global.ZEN_MODE_RINGER_LEVEL, AudioManager.RINGER_MODE_NORMAL)); + + // 4. apply zen off - verify ringer still silenced + when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT); + mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_OFF; + mZenModeHelperSpy.applyZenToRingerMode(); + verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT, + mZenModeHelperSpy.TAG); + } } diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java index f948f812676f..d35eb60916f3 100644 --- a/telephony/java/android/telephony/CellIdentityGsm.java +++ b/telephony/java/android/telephony/CellIdentityGsm.java @@ -120,7 +120,7 @@ public final class CellIdentityGsm extends CellIdentity { /** * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown - * @deprecated Use {@link #getMccStr} instead. + * @deprecated Use {@link #getMccString} instead. */ @Deprecated public int getMcc() { @@ -129,7 +129,7 @@ public final class CellIdentityGsm extends CellIdentity { /** * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown - * @deprecated Use {@link #getMncStr} instead. + * @deprecated Use {@link #getMncString} instead. */ @Deprecated public int getMnc() { @@ -176,14 +176,14 @@ public final class CellIdentityGsm extends CellIdentity { /** * @return Mobile Country Code in string format, null if unknown */ - public String getMccStr() { + public String getMccString() { return mMccStr; } /** * @return Mobile Network Code in string format, null if unknown */ - public String getMncStr() { + public String getMncString() { return mMncStr; } diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java index 5f1f4489e74e..2b8eb5f3cca6 100644 --- a/telephony/java/android/telephony/CellIdentityLte.java +++ b/telephony/java/android/telephony/CellIdentityLte.java @@ -125,7 +125,7 @@ public final class CellIdentityLte extends CellIdentity { /** * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown - * @deprecated Use {@link #getMccStr} instead. + * @deprecated Use {@link #getMccString} instead. */ @Deprecated public int getMcc() { @@ -134,7 +134,7 @@ public final class CellIdentityLte extends CellIdentity { /** * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown - * @deprecated Use {@link #getMncStr} instead. + * @deprecated Use {@link #getMncString} instead. */ @Deprecated public int getMnc() { @@ -179,14 +179,14 @@ public final class CellIdentityLte extends CellIdentity { /** * @return Mobile Country Code in string format, null if unknown */ - public String getMccStr() { + public String getMccString() { return mMccStr; } /** * @return Mobile Network Code in string format, null if unknown */ - public String getMncStr() { + public String getMncString() { return mMncStr; } diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java index 001d19f777fd..992545d62408 100644 --- a/telephony/java/android/telephony/CellIdentityTdscdma.java +++ b/telephony/java/android/telephony/CellIdentityTdscdma.java @@ -86,7 +86,7 @@ public final class CellIdentityTdscdma extends CellIdentity { * Get Mobile Country Code in string format * @return Mobile Country Code in string format, null if unknown */ - public String getMccStr() { + public String getMccString() { return mMccStr; } @@ -94,7 +94,7 @@ public final class CellIdentityTdscdma extends CellIdentity { * Get Mobile Network Code in string format * @return Mobile Network Code in string format, null if unknown */ - public String getMncStr() { + public String getMncString() { return mMncStr; } diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java index 1aa1715ee3e8..a5fd7dd97941 100644 --- a/telephony/java/android/telephony/CellIdentityWcdma.java +++ b/telephony/java/android/telephony/CellIdentityWcdma.java @@ -118,7 +118,7 @@ public final class CellIdentityWcdma extends CellIdentity { /** * @return 3-digit Mobile Country Code, 0..999, Integer.MAX_VALUE if unknown - * @deprecated Use {@link #getMccStr} instead. + * @deprecated Use {@link #getMccString} instead. */ @Deprecated public int getMcc() { @@ -127,7 +127,7 @@ public final class CellIdentityWcdma extends CellIdentity { /** * @return 2 or 3-digit Mobile Network Code, 0..999, Integer.MAX_VALUE if unknown - * @deprecated Use {@link #getMncStr} instead. + * @deprecated Use {@link #getMncString} instead. */ @Deprecated public int getMnc() { @@ -160,14 +160,14 @@ public final class CellIdentityWcdma extends CellIdentity { /** * @return Mobile Country Code in string version, null if unknown */ - public String getMccStr() { + public String getMccString() { return mMccStr; } /** * @return Mobile Network Code in string version, null if unknown */ - public String getMncStr() { + public String getMncString() { return mMncStr; } diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java index ec6a5ecbbd02..c86637186342 100644 --- a/tests/net/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java @@ -17,18 +17,24 @@ package android.net; import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P; import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -268,6 +274,120 @@ public class NetworkCapabilitiesTest { assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); } + @Test + public void testUnwantedCapabilities() { + NetworkCapabilities network = new NetworkCapabilities(); + + NetworkCapabilities request = new NetworkCapabilities(); + assertTrue("Request: " + request + ", Network:" + network, + request.satisfiedByNetworkCapabilities(network)); + + // Adding capabilities that doesn't exist in the network anyway + request.addUnwantedCapability(NET_CAPABILITY_WIFI_P2P); + request.addUnwantedCapability(NET_CAPABILITY_NOT_METERED); + assertTrue(request.satisfiedByNetworkCapabilities(network)); + assertArrayEquals(new int[] {NET_CAPABILITY_WIFI_P2P, NET_CAPABILITY_NOT_METERED}, + request.getUnwantedCapabilities()); + + // This is a default capability, just want to make sure its there because we use it below. + assertTrue(network.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + + // Verify that adding unwanted capability will effectively remove it from capability list. + request.addUnwantedCapability(NET_CAPABILITY_NOT_RESTRICTED); + assertTrue(request.hasUnwantedCapability(NET_CAPABILITY_NOT_RESTRICTED)); + assertFalse(request.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + + + // Now this request won't be satisfied because network contains NOT_RESTRICTED. + assertFalse(request.satisfiedByNetworkCapabilities(network)); + network.removeCapability(NET_CAPABILITY_NOT_RESTRICTED); + assertTrue(request.satisfiedByNetworkCapabilities(network)); + + // Verify that adding capability will effectively remove it from unwanted list + request.addCapability(NET_CAPABILITY_NOT_RESTRICTED); + assertTrue(request.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + assertFalse(request.hasUnwantedCapability(NET_CAPABILITY_NOT_RESTRICTED)); + + assertFalse(request.satisfiedByNetworkCapabilities(network)); + network.addCapability(NET_CAPABILITY_NOT_RESTRICTED); + assertTrue(request.satisfiedByNetworkCapabilities(network)); + } + + @Test + public void testEqualsNetCapabilities() { + int CAPABILITY = NET_CAPABILITY_MMS; // An arbitrary not mutable capability. + + NetworkCapabilities nc1 = new NetworkCapabilities(); + NetworkCapabilities nc2 = new NetworkCapabilities(); + assertTrue(nc1.equalsNetCapabilities(nc2)); + assertEquals(nc1, nc2); + + nc1.addCapability(CAPABILITY); + assertFalse(nc1.equalsNetCapabilities(nc2)); + assertNotEquals(nc1, nc2); + nc2.addCapability(CAPABILITY); + assertTrue(nc1.equalsNetCapabilities(nc2)); + assertEquals(nc1, nc2); + + nc1.addUnwantedCapability(CAPABILITY); + assertFalse(nc1.equalsNetCapabilities(nc2)); + nc2.addUnwantedCapability(CAPABILITY); + assertTrue(nc1.equalsNetCapabilities(nc2)); + + nc1.removeCapability(CAPABILITY); + assertFalse(nc1.equalsNetCapabilities(nc2)); + nc2.removeCapability(CAPABILITY); + assertTrue(nc1.equalsNetCapabilities(nc2)); + } + + @Test + public void testCombineCapabilities() { + NetworkCapabilities nc1 = new NetworkCapabilities(); + NetworkCapabilities nc2 = new NetworkCapabilities(); + + nc1.addUnwantedCapability(NET_CAPABILITY_CAPTIVE_PORTAL); + nc1.addCapability(NET_CAPABILITY_NOT_ROAMING); + assertNotEquals(nc1, nc2); + nc2.combineCapabilities(nc1); + assertEquals(nc1, nc2); + assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING)); + assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_CAPTIVE_PORTAL)); + + // This will effectively move NOT_ROAMING capability from required to unwanted for nc1. + nc1.addUnwantedCapability(NET_CAPABILITY_NOT_ROAMING); + + nc2.combineCapabilities(nc1); + // We will get this capability in both requested and unwanted lists thus this request + // will never be satisfied. + assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING)); + assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING)); + } + + @Test + public void testSetCapabilities() { + final int[] REQUIRED_CAPABILITIES = new int[] { + NET_CAPABILITY_INTERNET, NET_CAPABILITY_NOT_VPN }; + final int[] UNWANTED_CAPABILITIES = new int[] { + NET_CAPABILITY_NOT_RESTRICTED, NET_CAPABILITY_NOT_METERED + }; + + NetworkCapabilities nc1 = new NetworkCapabilities(); + NetworkCapabilities nc2 = new NetworkCapabilities(); + + nc1.setCapabilities(REQUIRED_CAPABILITIES, UNWANTED_CAPABILITIES); + assertArrayEquals(REQUIRED_CAPABILITIES, nc1.getCapabilities()); + + // Verify that setting and adding capabilities leads to the same object state. + nc2.clearAll(); + for (int cap : REQUIRED_CAPABILITIES) { + nc2.addCapability(cap); + } + for (int cap : UNWANTED_CAPABILITIES) { + nc2.addUnwantedCapability(cap); + } + assertEquals(nc1, nc2); + } + private void assertEqualsThroughMarshalling(NetworkCapabilities netCap) { Parcel p = Parcel.obtain(); netCap.writeToParcel(p, /* flags */ 0); diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index eb3a99ae6331..2ff92e651eda 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -101,6 +101,7 @@ cc_library_host_static { "io/ZipArchive.cpp", "link/AutoVersioner.cpp", "link/ManifestFixer.cpp", + "link/NoDefaultResourceRemover.cpp", "link/ProductFilter.cpp", "link/PrivateAttributeMover.cpp", "link/ReferenceLinker.cpp", diff --git a/tools/aapt2/OWNERS b/tools/aapt2/OWNERS index d76233ec78ba..23ec5ab0d1f3 100644 --- a/tools/aapt2/OWNERS +++ b/tools/aapt2/OWNERS @@ -1,2 +1,2 @@ set noparent -adamlesinski@google.com +toddke@google.com diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp index 95bf9210ba97..d0faac30425a 100644 --- a/tools/aapt2/ResourceTable.cpp +++ b/tools/aapt2/ResourceTable.cpp @@ -162,7 +162,7 @@ struct ConfigKey { const StringPiece& product; }; -bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) { +bool lt_config_key_ref(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) { int cmp = lhs->config.compare(*rhs.config); if (cmp == 0) { cmp = StringPiece(lhs->product).compare(rhs.product); @@ -172,8 +172,8 @@ bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs, const Confi ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config, const StringPiece& product) { - auto iter = - std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, ltConfigKeyRef); + auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, + lt_config_key_ref); if (iter != values.end()) { ResourceConfigValue* value = iter->get(); if (value->config == config && StringPiece(value->product) == product) { @@ -185,8 +185,8 @@ ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config, ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& config, const StringPiece& product) { - auto iter = - std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, ltConfigKeyRef); + auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, + lt_config_key_ref); if (iter != values.end()) { ResourceConfigValue* value = iter->get(); if (value->config == config && StringPiece(value->product) == product) { @@ -220,15 +220,16 @@ std::vector<ResourceConfigValue*> ResourceEntry::FindAllValues(const ConfigDescr return results; } -std::vector<ResourceConfigValue*> ResourceEntry::FindValuesIf( - const std::function<bool(ResourceConfigValue*)>& f) { - std::vector<ResourceConfigValue*> results; - for (auto& configValue : values) { - if (f(configValue.get())) { - results.push_back(configValue.get()); +bool ResourceEntry::HasDefaultValue() const { + const ConfigDescription& default_config = ConfigDescription::DefaultConfig(); + + // The default config should be at the top of the list, since the list is sorted. + for (auto& config_value : values) { + if (config_value->config == default_config) { + return true; } } - return results; + return false; } // The default handler for collisions. diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h index 374fe1ea66a1..8534eaaf0366 100644 --- a/tools/aapt2/ResourceTable.h +++ b/tools/aapt2/ResourceTable.h @@ -104,13 +104,26 @@ class ResourceEntry { explicit ResourceEntry(const android::StringPiece& name) : name(name.to_string()) {} ResourceConfigValue* FindValue(const ConfigDescription& config); + ResourceConfigValue* FindValue(const ConfigDescription& config, const android::StringPiece& product); + ResourceConfigValue* FindOrCreateValue(const ConfigDescription& config, const android::StringPiece& product); std::vector<ResourceConfigValue*> FindAllValues(const ConfigDescription& config); - std::vector<ResourceConfigValue*> FindValuesIf( - const std::function<bool(ResourceConfigValue*)>& f); + + template <typename Func> + std::vector<ResourceConfigValue*> FindValuesIf(Func f) { + std::vector<ResourceConfigValue*> results; + for (auto& config_value : values) { + if (f(config_value.get())) { + results.push_back(config_value.get()); + } + } + return results; + } + + bool HasDefaultValue() const; private: DISALLOW_COPY_AND_ASSIGN(ResourceEntry); diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index 12ab88345411..0839f6ff87ad 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -55,6 +55,7 @@ #include "java/ProguardRules.h" #include "link/Linkers.h" #include "link/ManifestFixer.h" +#include "link/NoDefaultResourceRemover.h" #include "link/ReferenceLinker.h" #include "link/TableMerger.h" #include "link/XmlCompatVersioner.h" @@ -1785,6 +1786,14 @@ class LinkCommand { util::make_unique<FeatureSplitSymbolTableDelegate>(context_)); } + // Before we process anything, remove the resources whose default values don't exist. + // We want to force any references to these to fail the build. + if (!NoDefaultResourceRemover{}.Consume(context_, &final_table_)) { + context_->GetDiagnostics()->Error(DiagMessage() + << "failed removing resources with no defaults"); + return 1; + } + ReferenceLinker linker; if (!linker.Consume(context_, &final_table_)) { context_->GetDiagnostics()->Error(DiagMessage() << "failed linking references"); diff --git a/tools/aapt2/compile/InlineXmlFormatParser.cpp b/tools/aapt2/compile/InlineXmlFormatParser.cpp index 238e339c05af..79b0933a089f 100644 --- a/tools/aapt2/compile/InlineXmlFormatParser.cpp +++ b/tools/aapt2/compile/InlineXmlFormatParser.cpp @@ -65,10 +65,6 @@ class Visitor : public xml::PackageAwareVisitor { } const ResourceName& name = ref.value().name.value(); - - // Use an empty string for the compilation package because we don't want to default to - // the local package if the user specified name="style" or something. This should just - // be the default namespace. Maybe<xml::ExtractedPackage> maybe_pkg = TransformPackageAlias(name.package); if (!maybe_pkg) { context_->GetDiagnostics()->Error(DiagMessage(src) @@ -83,8 +79,15 @@ class Visitor : public xml::PackageAwareVisitor { InlineDeclaration decl; decl.el = el; decl.attr_name = name.entry; - if (!pkg.package.empty()) { - decl.attr_namespace_uri = xml::BuildPackageNamespace(pkg.package, private_namespace); + + // We need to differentiate between no-namespace defined, or the alias resolves to an empty + // package, which means we must use the res-auto schema. + if (!name.package.empty()) { + if (pkg.package.empty()) { + decl.attr_namespace_uri = xml::kSchemaAuto; + } else { + decl.attr_namespace_uri = xml::BuildPackageNamespace(pkg.package, private_namespace); + } } inline_declarations_.push_back(std::move(decl)); diff --git a/tools/aapt2/compile/InlineXmlFormatParser_test.cpp b/tools/aapt2/compile/InlineXmlFormatParser_test.cpp index 2b4ab96d5c3a..ca4e01a30215 100644 --- a/tools/aapt2/compile/InlineXmlFormatParser_test.cpp +++ b/tools/aapt2/compile/InlineXmlFormatParser_test.cpp @@ -184,4 +184,42 @@ TEST(InlineXmlFormatParserTest, ExtractNestedXmlResources) { // Confirm that all of the nested inline xmls are parsed out. ASSERT_THAT(parser.GetExtractedInlineXmlDocuments(), SizeIs(8u)); } + +TEST(InlineXmlFormatParserTest, ExtractIntoAppAttribute) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); + std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"( + <parent xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="app:foo"> + <child /> + </aapt:attr> + </parent>)"); + + doc->file.name = test::ParseNameOrDie("layout/main"); + + InlineXmlFormatParser parser; + ASSERT_TRUE(parser.Consume(context.get(), doc.get())); + + ASSERT_THAT(doc->root, NotNull()); + EXPECT_THAT(doc->root->FindAttribute(xml::kSchemaAuto, "foo"), NotNull()); +} + +TEST(InlineXmlFormatParserTest, ExtractIntoNoNamespaceAttribute) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); + std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"( + <parent xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="foo"> + <child /> + </aapt:attr> + </parent>)"); + + doc->file.name = test::ParseNameOrDie("layout/main"); + + InlineXmlFormatParser parser; + ASSERT_TRUE(parser.Consume(context.get(), doc.get())); + + ASSERT_THAT(doc->root, NotNull()); + EXPECT_THAT(doc->root->FindAttribute({}, "foo"), NotNull()); +} + } // namespace aapt diff --git a/tools/aapt2/link/NoDefaultResourceRemover.cpp b/tools/aapt2/link/NoDefaultResourceRemover.cpp new file mode 100644 index 000000000000..cfb4b26aba02 --- /dev/null +++ b/tools/aapt2/link/NoDefaultResourceRemover.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "link/NoDefaultResourceRemover.h" + +#include <algorithm> + +#include "ResourceTable.h" + +namespace aapt { + +static bool IsDefaultConfigRequired(const ConfigDescription& config) { + // We don't want to be overzealous with resource removal, so have strict requirements. + // If a resource defines a value for a locale-only configuration, the default configuration is + // required. + if (ConfigDescription::DefaultConfig().diff(config) == ConfigDescription::CONFIG_LOCALE) { + return true; + } + return false; +} + +static bool KeepResource(const std::unique_ptr<ResourceEntry>& entry) { + if (entry->visibility.level == Visibility::Level::kPublic) { + // Removing a public API without the developer knowing is bad, so just leave this here for now. + return true; + } + + if (entry->HasDefaultValue()) { + // There is a default value, no removal needed. + return true; + } + + // There is no default value defined, check if removal is required. + for (const auto& config_value : entry->values) { + if (IsDefaultConfigRequired(config_value->config)) { + return false; + } + } + return true; +} + +bool NoDefaultResourceRemover::Consume(IAaptContext* context, ResourceTable* table) { + const ConfigDescription default_config = ConfigDescription::DefaultConfig(); + for (auto& pkg : table->packages) { + for (auto& type : pkg->types) { + const auto end_iter = type->entries.end(); + const auto new_end_iter = + std::stable_partition(type->entries.begin(), end_iter, KeepResource); + for (auto iter = new_end_iter; iter != end_iter; ++iter) { + const ResourceName name(pkg->name, type->type, (*iter)->name); + IDiagnostics* diag = context->GetDiagnostics(); + diag->Warn(DiagMessage() << "removing resource " << name + << " without required default value"); + if (context->IsVerbose()) { + diag->Note(DiagMessage() << " did you forget to remove all definitions?"); + for (const auto& config_value : (*iter)->values) { + if (config_value->value != nullptr) { + diag->Note(DiagMessage(config_value->value->GetSource()) << "defined here"); + } + } + } + } + + type->entries.erase(new_end_iter, type->entries.end()); + } + } + return true; +} + +} // namespace aapt diff --git a/tools/aapt2/link/NoDefaultResourceRemover.h b/tools/aapt2/link/NoDefaultResourceRemover.h new file mode 100644 index 000000000000..7582ef3f7516 --- /dev/null +++ b/tools/aapt2/link/NoDefaultResourceRemover.h @@ -0,0 +1,45 @@ +/* + * 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. + */ + +#ifndef AAPT_LINK_NODEFAULTRESOURCEREMOVER_H +#define AAPT_LINK_NODEFAULTRESOURCEREMOVER_H + +#include "android-base/macros.h" + +#include "process/IResourceTableConsumer.h" + +namespace aapt { + +// Removes any resource for which there exists no definition for the default configuration, where +// for that resource type, a definition is required. +// +// The obvious example is when defining localized strings. If a string in the default configuration +// has its name changed, the translations for that string won't be changed but will still cause +// the generated R class to contain the old string name. This will cause breakages in apps that +// still rely on the old name when the translations are updated. +class NoDefaultResourceRemover : public IResourceTableConsumer { + public: + NoDefaultResourceRemover() = default; + + bool Consume(IAaptContext* context, ResourceTable* table) override; + + private: + DISALLOW_COPY_AND_ASSIGN(NoDefaultResourceRemover); +}; + +} // namespace aapt + +#endif // AAPT_LINK_NODEFAULTRESOURCEREMOVER_H diff --git a/tools/aapt2/link/NoDefaultResourceRemover_test.cpp b/tools/aapt2/link/NoDefaultResourceRemover_test.cpp new file mode 100644 index 000000000000..943709a2af12 --- /dev/null +++ b/tools/aapt2/link/NoDefaultResourceRemover_test.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "link/NoDefaultResourceRemover.h" + +#include "test/Test.h" + +namespace aapt { + +TEST(NoDefaultResourceRemoverTest, RemoveEntryWithNoDefaultAndOnlyLocales) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); + std::unique_ptr<ResourceTable> table = + test::ResourceTableBuilder() + .SetPackageId("android", 0x01) + .AddSimple("android:string/foo") + .AddSimple("android:string/foo", test::ParseConfigOrDie("en-rGB")) + .AddSimple("android:string/foo", test::ParseConfigOrDie("fr-rFR")) + .AddSimple("android:string/bar", test::ParseConfigOrDie("en-rGB")) + .AddSimple("android:string/bar", test::ParseConfigOrDie("fr-rFR")) + .AddSimple("android:string/bat", test::ParseConfigOrDie("en-rGB-xhdpi")) + .AddSimple("android:string/bat", test::ParseConfigOrDie("fr-rFR-hdpi")) + .AddSimple("android:string/baz", test::ParseConfigOrDie("en-rGB")) + .AddSimple("android:string/baz", test::ParseConfigOrDie("fr-rFR")) + .SetSymbolState("android:string/baz", ResourceId(0x01020002), Visibility::Level::kPublic) + .Build(); + + NoDefaultResourceRemover remover; + ASSERT_TRUE(remover.Consume(context.get(), table.get())); + + EXPECT_TRUE(table->FindResource(test::ParseNameOrDie("android:string/foo"))); + EXPECT_FALSE(table->FindResource(test::ParseNameOrDie("android:string/bar"))); + EXPECT_TRUE(table->FindResource(test::ParseNameOrDie("android:string/bat"))); + EXPECT_TRUE(table->FindResource(test::ParseNameOrDie("android:string/baz"))); +} + +} // namespace aapt diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp index 486b53ada6bb..ca46d539fe3c 100644 --- a/tools/aapt2/xml/XmlDom_test.cpp +++ b/tools/aapt2/xml/XmlDom_test.cpp @@ -154,6 +154,12 @@ class TestVisitor : public PackageAwareVisitor { EXPECT_THAT(TransformPackageAlias("two"), Eq(make_value(ExtractedPackage{"com.two", false}))); EXPECT_THAT(TransformPackageAlias("three"), Eq(make_value(ExtractedPackage{"com.three", false}))); + } else if (el->name == "View4") { + EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false}))); + EXPECT_THAT(TransformPackageAlias("two"), Eq(make_value(ExtractedPackage{"com.two", false}))); + EXPECT_THAT(TransformPackageAlias("three"), + Eq(make_value(ExtractedPackage{"com.three", false}))); + EXPECT_THAT(TransformPackageAlias("four"), Eq(make_value(ExtractedPackage{"", true}))); } } }; @@ -162,7 +168,9 @@ TEST(XmlDomTest, PackageAwareXmlVisitor) { std::unique_ptr<XmlResource> doc = test::BuildXmlDom(R"( <View1 xmlns:one="http://schemas.android.com/apk/res/com.one"> <View2 xmlns:two="http://schemas.android.com/apk/res/com.two"> - <View3 xmlns:three="http://schemas.android.com/apk/res/com.three" /> + <View3 xmlns:three="http://schemas.android.com/apk/res/com.three"> + <View4 xmlns:four="http://schemas.android.com/apk/res-auto" /> + </View3> </View2> </View1>)"); diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp index 9183918fcc63..e396a638927e 100644 --- a/tools/incident_section_gen/main.cpp +++ b/tools/incident_section_gen/main.cpp @@ -203,6 +203,18 @@ static inline Destination getFieldDest(const FieldDescriptor* field) { getMessageDest(field->message_type(), fieldDest); } +// Converts Destination to a string. +static inline string getDestString(const Destination dest) { + switch (dest) { + case DEST_AUTOMATIC: return "AUTOMATIC"; + case DEST_LOCAL: return "LOCAL"; + case DEST_EXPLICIT: return "EXPLICIT"; + // UNSET is considered EXPLICIT by default. + case DEST_UNSET: return "EXPLICIT"; + default: return "UNKNOWN"; + } +} + // Get Names =========================================================================================== static inline string getFieldName(const FieldDescriptor* field) { // replace . with double underscores to avoid name conflicts since fields use snake naming convention @@ -218,7 +230,7 @@ static inline string getMessageName(const Descriptor* descriptor, const Destinat // IsDefault ============================================================================================ // Returns true if a field is default. Default is defined as this field has same dest as its containing message. -// For message fields, it only looks at its field tag and own default mesaage tag, doesn't recursively go deeper. +// For message fields, it only looks at its field tag and own default message tag, doesn't recursively go deeper. static inline bool isDefaultField(const FieldDescriptor* field, const Destination containerDest) { Destination fieldDest = getFieldDest(field); if (field->type() != FieldDescriptor::TYPE_MESSAGE) { @@ -503,11 +515,12 @@ static string replace_string(const string& str, const char replace, const char w return result; } -static void generateCsv(Descriptor const* descriptor, const string& indent, set<string>* parents) { +static void generateCsv(Descriptor const* descriptor, const string& indent, set<string>* parents, const Destination containerDest = DEST_UNSET) { DebugStringOptions options; options.include_comments = true; for (int i=0; i<descriptor->field_count(); i++) { const FieldDescriptor* field = descriptor->field(i); + const Destination fieldDest = getFieldDest(field); stringstream text; if (field->type() == FieldDescriptor::TYPE_MESSAGE) { text << field->message_type()->name(); @@ -515,11 +528,18 @@ static void generateCsv(Descriptor const* descriptor, const string& indent, set< text << field->type_name(); } text << " " << field->name(); + text << " (PRIVACY="; + if (isDefaultField(field, containerDest)) { + text << getDestString(containerDest); + } else { + text << getDestString(fieldDest); + } + text << ")"; printf("%s%s,\n", indent.c_str(), replace_string(text.str(), '\n', ' ').c_str()); if (field->type() == FieldDescriptor::TYPE_MESSAGE && parents->find(field->message_type()->full_name()) == parents->end()) { parents->insert(field->message_type()->full_name()); - generateCsv(field->message_type(), indent + ",", parents); + generateCsv(field->message_type(), indent + ",", parents, fieldDest); parents->erase(field->message_type()->full_name()); } } @@ -548,7 +568,7 @@ int main(int argc, char const *argv[]) || field->number() == sectionId) { set<string> parents; printf("%s\n", field->name().c_str()); - generateCsv(field->message_type(), "", &parents); + generateCsv(field->message_type(), "", &parents, getFieldDest(field)); break; } } diff --git a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java index 2052f155abee..eca840644fc6 100644 --- a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java +++ b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java @@ -116,9 +116,12 @@ public class DiscoverySessionCallback { * Called when a discovery (publish or subscribe) operation results in a * service discovery. Called when a Subscribe service was configured with a range requirement * {@link SubscribeConfig.Builder#setMinDistanceMm(int)} and/or - * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)}. A discovery will only be declared - * (i.e. this callback called) if the range of the publisher is within the specified distance - * constraints. + * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)} and the Publish service was configured + * with {@link PublishConfig.Builder#setRangingEnabled(boolean)}. + * <p> + * If either Publisher or Subscriber does not enable Ranging, or if Ranging is temporarily + * disabled by the underlying device, service discovery proceeds without ranging and the + * {@link #onServiceDiscovered(PeerHandle, byte[], List)} is called. * * @param peerHandle An opaque handle to the peer matching our discovery operation. * @param serviceSpecificInfo The service specific information (arbitrary diff --git a/wifi/java/android/net/wifi/aware/PeerHandle.java b/wifi/java/android/net/wifi/aware/PeerHandle.java index b525212ed0a8..8ae4b5af6325 100644 --- a/wifi/java/android/net/wifi/aware/PeerHandle.java +++ b/wifi/java/android/net/wifi/aware/PeerHandle.java @@ -18,11 +18,20 @@ package android.net.wifi.aware; /** * Opaque object used to represent a Wi-Fi Aware peer. Obtained from discovery sessions in - * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)}, used - * when sending messages e,g, {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])}, + * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)} or + * received messages in {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, byte[])}, and + * used when sending messages e,g, {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])}, * or when configuring a network link to a peer, e.g. * {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)} or * {@link DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)}. + * <p> + * Note that while a {@code PeerHandle} can be used to track a particular peer (i.e. you can compare + * the values received from subsequent messages) - it is good practice not to rely on it. Instead + * use an application level peer identifier encoded in the message, + * {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])}, and/or in the Publish + * configuration's service-specific information field, + * {@link PublishConfig.Builder#setServiceSpecificInfo(byte[])}, or match filter, + * {@link PublishConfig.Builder#setMatchFilter(java.util.List)}. */ public class PeerHandle { /** @hide */ diff --git a/wifi/java/android/net/wifi/aware/PublishConfig.java b/wifi/java/android/net/wifi/aware/PublishConfig.java index 7a0250bf0700..d43f72740be4 100644 --- a/wifi/java/android/net/wifi/aware/PublishConfig.java +++ b/wifi/java/android/net/wifi/aware/PublishConfig.java @@ -365,9 +365,8 @@ public final class PublishConfig implements Parcelable { * {@link SubscribeConfig.Builder#setMaxDistanceMm(int)} to specify a minimum and/or * maximum distance at which discovery will be triggered. * <p> - * Optional. Disabled by default - i.e. any peer which attempts to measure distance to this - * device will be refused. If the peer has ranging enabled (using the - * {@link SubscribeConfig} APIs listed above, it will never discover this device. + * Optional. Disabled by default - i.e. any peer attempt to measure distance to this device + * will be refused and discovery will proceed without ranging constraints. * <p> * The device must support Wi-Fi RTT for this feature to be used. Feature support is checked * as described in {@link android.net.wifi.rtt}. diff --git a/wifi/java/android/net/wifi/aware/SubscribeConfig.java b/wifi/java/android/net/wifi/aware/SubscribeConfig.java index 2eab76a10cb4..2ec3b704f0f9 100644 --- a/wifi/java/android/net/wifi/aware/SubscribeConfig.java +++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.java @@ -415,17 +415,23 @@ public final class SubscribeConfig implements Parcelable { /** * Configure the minimum distance to a discovered publisher at which to trigger a discovery - * notification. I.e. discovery will only be triggered if we've found a matching publisher + * notification. I.e. discovery will be triggered if we've found a matching publisher * (based on the other criteria in this configuration) <b>and</b> the distance to the - * publisher is > the value specified in this API. + * publisher is larger than the value specified in this API. Can be used in conjunction with + * {@link #setMaxDistanceMm(int)} to specify a geofence, i.e. discovery with min < + * distance < max. * <p> - * Can be used in conjunction with {@link #setMaxDistanceMm(int)} to specify a geo-fence, - * i.e. discovery with min < distance < max. + * For ranging to be used in discovery it must also be enabled on the publisher using + * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. However, ranging may + * not be available or enabled on the publisher or may be temporarily disabled on either + * subscriber or publisher - in such cases discovery will proceed without ranging. * <p> - * If this API is called, the subscriber requires ranging. In such a case, the publisher - * peer must enable ranging using - * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. Otherwise discovery will - * never be triggered. + * When ranging is enabled and available on both publisher and subscriber and a service + * is discovered based on geofence constraints the + * {@link DiscoverySessionCallback#onServiceDiscoveredWithinRange(PeerHandle, byte[], List, int)} + * is called, otherwise the + * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], List)} + * is called. * <p> * The device must support Wi-Fi RTT for this feature to be used. Feature support is checked * as described in {@link android.net.wifi.rtt}. @@ -444,17 +450,23 @@ public final class SubscribeConfig implements Parcelable { /** * Configure the maximum distance to a discovered publisher at which to trigger a discovery - * notification. I.e. discovery will only be triggered if we've found a matching publisher + * notification. I.e. discovery will be triggered if we've found a matching publisher * (based on the other criteria in this configuration) <b>and</b> the distance to the - * publisher is < the value specified in this API. + * publisher is smaller than the value specified in this API. Can be used in conjunction + * with {@link #setMinDistanceMm(int)} to specify a geofence, i.e. discovery with min < + * distance < max. * <p> - * Can be used in conjunction with {@link #setMinDistanceMm(int)} to specify a geo-fence, - * i.e. discovery with min < distance < max. + * For ranging to be used in discovery it must also be enabled on the publisher using + * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. However, ranging may + * not be available or enabled on the publisher or may be temporarily disabled on either + * subscriber or publisher - in such cases discovery will proceed without ranging. * <p> - * If this API is called, the subscriber requires ranging. In such a case, the publisher - * peer must enable ranging using - * {@link PublishConfig.Builder#setRangingEnabled(boolean)}. Otherwise discovery will - * never be triggered. + * When ranging is enabled and available on both publisher and subscriber and a service + * is discovered based on geofence constraints the + * {@link DiscoverySessionCallback#onServiceDiscoveredWithinRange(PeerHandle, byte[], List, int)} + * is called, otherwise the + * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], List)} + * is called. * <p> * The device must support Wi-Fi RTT for this feature to be used. Feature support is checked * as described in {@link android.net.wifi.rtt}. |